Git Product home page Git Product logo

b-com-software-basis / xpcf Goto Github PK

View Code? Open in Web Editor NEW
2.0 3.0 4.0 3.22 MB

XPCF stands for Cross-Platform Component Framework. It provides base functionalities to handle component interfaces and method introspection, dependency injection and initial configuration.

License: Other

C++ 86.90% QMake 5.55% C 0.18% Batchfile 1.93% Shell 2.59% CMake 0.02% SWIG 1.60% Starlark 0.14% C# 1.09%
introspection dependency-injection component-introspection uuid factories modern-cpp architectural-patterns cpp17

xpcf's Introduction

XPCF Cross-Platform Component Framework

GitHub version LoC

XPCF is a lightweight cross platform component framework (it is similar to the well known COM model).

It is designed to provide dependency injection, with clarity, simplicity and light code base in mind.

It implements the abstractfactory and the factory design patterns (through the ComponentManager and ComponentFactory classes, and through IFactory interface).

It also provides a safe toolkit to avoid common c++ design issues such as :

  • ensure use of one unique shared_ptr type (even on platforms with several STLs : the SRef class is guaranteed to be from one source only through boost binary)
  • ensure correct memory handling (avoid memory leaks)
  • provide parallel helpers such as tasks, fifo, circular buffers, delays..
  • a common configuration design : configuring a component doesn't require to write serialization code

XPCF also provides remoting automation by generating grpc remoting code from XPCF interfaces, using a dedicated DSL (Domain Specific Language) - see XPCF remoting architecture chapter.

Changelog

Learn about the latest improvements.

Definitions

Module

A module consists in a shared library hosting a set of factories to create components/services

Provides:

  • introspection to figure out which components are available in the module

  • separate implementation from interface : creates the concrete implementation and binds it to the IComponentIntrospect interface

  • component creation

  • service instanciation

Component/Service interface

A component/service interface in the component framework context is :

  • based on C++ virtual class

  • uniquely identified by an UUID (128-bit number guaranteed to be unique based on timestamps, HW addresses, random seeds)

  • mandatory derives from IComponentIntrospect (the base interface of all XPCF interfaces)

IComponentIntrospect interface

  • is a special interface

  • does not derive from any other interface

Provides:

  • component introspection to query interfaces implemented by the component

  • functional reference counting

Special Notes about queryInterface :

  • queryInterface from interface A to interface B, must be able to queryInterface back from interface B to interface A

  • queryInterface from interface A to the IComponentIntrospect interface, must equal the queryInterface from interface B to the IComponentIntrospect interface

Components and Services

An implementation of one or more interfaces identified by a Component UUID

NOTE: The introspection interface is the same for both components and services

component

  • a class that can have several instances over the application lifetime

  • each instance is unloaded (destroyed) when no more reference exists upon one of its interfaces

service

  • a component singleton that only has one instance at any given time

  • is never unloaded

Schema

 ___________________________________________________
| Module #1                                      |
| entry point => XPCF_getComponent                  |
|           ______________________________          |
|          | Component #UUIDC1            |         |
|          |        ___________________   |         |
|          | I0 -> | I0 implementation |  |         |
|          |        -------------------   |         |
|          |                              |         |
|          |        ___________________   |         |
|          | I1 -> | I1 implementation |  |         |
|          |        -------------------   |         |
|           ------------------------------          |
|                                                   |
|           ______________________________          |
|          | Component #UUIDC2            |         |
|          |        ___________________   |         |
|          | I0 -> | I0 implementation |  |         |
|          |        -------------------   |         |
|          |                              |         |
|          |        ___________________   |         |
|          | I2 -> | I2 implementation |  |         |
|          |        -------------------   |         |
|           ------------------------------          |
|                                                   |
| entry point => XPCF_getService                    |
|           ______________________________          |
|          | Service #UUIDS1              |         |
|          |        ___________________   |         |
|          | I3 -> | I3 implementation |  |         |
|          |        -------------------   |         |
|           ------------------------------          |
 ---------------------------------------------------

Framework functionalities

This framework provides the following concepts :

  • an "in-code" dependency injection factory to create components and bind them to a specific interface

  • a component abstract factory to load components at runtime from modules (named ModuleManager)

  • increase modularity with better separation of concerns through interface discovery (using component introspection)

  • a centralized description of modules, components and interfaces (with a dedicated configuration file)

  • module introspection to figure out the components contained and the interfaces they implement

  • secure memory management using smart pointers for components (prevents from memory leaks, dangling null pointer)

  • component and service configuration at creation through abstract variant parameters

  • support for both components and services

  • support for Domain Specific Language through the "IFeatures" interface (for instance, a component can declare "opencv" and "cuda" features as it is an opencv/cuda accelerated implementation of a keypointdetector)

Note: Variant parameters for configuration are a set of parameters. Each parameter is a pair of type, value. Supported types include long, unsigned long, double, string or another subset of parameters. Each parameter has an access definition : in, out or inout.

Aliases

  • an \<aliases\> section can be declared in any xpcf-registry or xpcf-configuration node to declare named alias to uuids for an interface, a component or a module.
  • aliases can be used in <factory>...</factory> or <properties>...</properties> sections to refer indifferently to components, interfaces either through their uuids or alias. Note: an alias name must be unique in a ComponentManager context.
  • autoalias mode: makes an alias between an interface/component name and its uuid. Provides the ability to use the alias in place of the uuid in factory bindings and in properties configuration (only supported with new <properties> semantic). autoalias creates an alias when the first occurence of a name is met (particularly important for interfaces). This mode is enabled when autoAlias xml attribute is set to "true" in <xpcf-registry autoAlias="..."> or <xpcf-configuration autoAlias="..."> Note:
  • when a name is encountered twice, the second occurence is ignored (it should always be the same alias which is true for interfaces)
  • any ambiguity must be resolved with explicit aliasing through an xml <aliases> node
  • a potential evolution will be to "namespace" the component name with the module name to reduce naming conflicts

Component dependency injection

  • compile-time injection is provided through IInjectable interface
  • local components can be used during component injection once registered to XPCF using bindLocal or bind methods (components defined in the library's own code or in libraries linked, but not from dynamically loaded modules). The bind method accepts a factory method to create the concrete component instance -> ComponentBase implements IInjectable and provides injection capabilities to every xpcf components
  • recursive dependency injection and configuration is supported
  • injection errors are handled with InjectionException, InjectableNotFoundException and InjectableDeclarationException exceptions
  • declarative injection (configuration time injection) is provided in registry files through <factory>...</factory> declarations (refer to ````doc/xpcf-registry-sample.xml``` for the semantic)
  • autobind mode is always enabled. While parsing the <module> nodes, it automatically "wires" (binds) components to their declared interfaces. autobind also works upon module introspection made at compile time using IComponentManager::loadModules or IComponentManager::loadModuleMetadata. Note: as autobind is a default behavior, only redundant/specific binds must be declared in <factory ... /> node. Otherwise, default binds are created from components definitions. Note: Autobinding ignores IComponentIntrospect, IConfigurable and IInjectable XPCF interfaces. Autobinding handles wiring from user's interfaces to components.

IComponentManager provides:

  • global "inject" methods helper
  • dependency injection through default binding while parsing the <module> nodes, or through <factory><bindings>... declarations.
  • structured injection. See the semantic in xpcf-registry-sample.xml: node <injects>.
  • named binding injection ability either from configuration (using <factory>...</factory> node) or at compile time
  • injection from bindings upon createComponent from component type trait or uuid
  • concrete component instance resolution from its interface ( or service ) type traits or uuid (and optional name) Upon creation of the concrete instance injection of the component injectables is made when needed through "resolve" methods, and each injectable is configured. Finally the concrete instance is configured and returned to the caller.

XPCF Factories

From 2.5.0, XPCF now supports several creation contexts through the "IFactory" concept. The IComponentManager interface provides access to its inner Factory. From this factory, it is possible to create new factories. New factories are created with a context mode within :

  • Empty: creates a new and empty factory
  • Cloned: creates a factory with a copy of bindings existing in the original factory. Later bindings, configuration ... will only be valid within this cloned factory.
  • Shared: creates a factory that shares the bindings, configuration definition. Only component instances are local to the factory. Any later bind or configuration update will be share between the new and the original factory.

XPCF remoting architecture

A new feature in xpcf 2.5.0 version is the ability to get remote versions of components almost "out of the box".

The remoting functionality works for any xpcf interface that :

  • uses datastructures that declare one of the serialization methods exposed through xpcf specifications (one of the methods provided in xpcf/remoting/ISerializable.h) - in 2.5.0 only boost serialization is handled, upcoming minor releases of xpcf will provide support for the other methods
  • uses base c++ types only

Note : pointers are not handled as they can handle arrays ... hence a pointer will need a size, but no presumptions to detect such cases can be done. Anyway, interfaces represent contracts between a user and a service, hence interfaces should be self explanatory. In this context, relying on pointersd doesn't explicit the expected behavior, hence interfaces should rely on structured types either std ones (string, vectors ...) or user defined ones (struct, classes).

xpcf remoting DSL

xpcf provides a Domain specific language through the use of c++ user defined attributes to ease the generation, those attributes can be used on interfaces or on methods of an interface, depending on the attribute.

DSL description

Attribute Argument type Apply to Semantic
[[xpcf::ignore]] none class or method level specify that the corresponding class/method must be ignored while generating remoting code
[[xpcf::clientUUID("uuid_string")]] string class level specify the xpcf remoting client UUID
[[xpcf::serverUUID("uuid_string")]] string class level specify the xpcf remoting server UUID
[[grpc::noCompression)]] none class or method level disable grpc compression for a whole interface or for a method (compression code generation is not made for the targeted item)
[[grpc::server_streaming]], [[grpc::client_streaming]] or [[grpc::streaming]] none method level
[[grpc::request("requestMessageName")]] string method level optionally set grpc request message name
[[grpc::response("responseMessageName")]] string method level optionally set grpc response message name
[[grpc::rpcName("rpcname")]] string method level optionally set grpc method rpc name

Xpcf remoting tools

xpcf provides several tools to ease the remoting of components :

  1. xpcf_grpc_gen : parses the interfaces and generates the needed remoting code for each inteface in a project.

    • From an interface, the tool creates :
      • grpc request and response messages with each input/output type translated to grpc types
      • a grpc service containing rpc : one for each method of the interface
      • a proxy component that inherits from the interface. This component transforms the interface input/output types to grpc request/response and calls the grpc service and rpc method corresponding with the method interface. This component is configured with a channel url (address and port of the server hosting the concrete code) and a grpc credential.
      • a server component inheriting from IGrpcService. It also implements the grpc::service class, and the concrete xpcf component is injected to this component. Each rpc method of the grpc::service class is implemented and translates grpc request/response to the xpcf component input/output data types, and calls the xpcf component corresponding method
    • From the project the tool creates :
      • a Qt creator development project to compile all the generated code in an xpcf module
      • a client and a server xpcf xml configuration file
  2. xpcf_grpc_server : it is a xpcf application that hosts any IGrpcService enabled component.

To come: xpcf_grpc_linter : this tool will be used at compilation time to validate the xpcf remoting DSL.

XPCF Qt Creator wizards

To help developers create efficiently XPCF based applications, several Qt Creator wizards are provided. Wizards are located in the XPCF release in xpcf/version/wizards, or in the xpcf github repository in the wizards folder. To install the wizards, first install Qt Creator, then launch install.bat (windows platform) or install.sh (linux or macosX platforms).

XPCF Module wizard

This wizard creates a complete Qt Project to build a shared library containing a xpcf module. It contains a skeleton module_main.cpp file with the main XPCF entry point. It depends on qmake rules (from the builddefs-qmake github repository, installed with the remaken package manager tool from remaken github repository ).

XPCF Interface wizard

This wizard creates a xpcf interface header file. This file is the public API. Any component interested to provide this interface functionality will use this header file. The developer only need to add its methods to the interface.

XPCF Component wizard

This wizard creates a xpcf component header and source file. The developer only need to override and implement the methods defined in the interfaces provided by the component. The component header file doesn't need to be exposed. Only the interfaces implemented are part of the API.

XPCF Application wizard

This wizard creates a complete Qt Project to build an application that use xpcf module and components.

XPCF configuration file format

XPCF components can be found, configured and injected with a xml configuration file.

###Node tree The supported xml structure is (in each xml node, "..." symbolizes attributes):

<xpcf-registry>    
    <module ... > <!-- module declaration -->   
        <component ... > <!-- component declaration --> 
            <interface ... />  <!-- interface declaration --> 
        </component>
        <!-- ... other component declarations -->            
    </module>    

    <aliases>   
        <alias ... /> 
        <!-- ... other alias declarations -->   
    </aliases>   

    <factory>
        <!-- default bindings -->   
        <bindings> 
            <bind ... />
            <!-- ... other binding declarations -->
        </bindings>
        <!-- custom bindings : explicit injection for a particular component class -->       
        <injects> 
            <inject ... >
                <bind ... />
                <!-- ... other binding declarations -->
            </inject>
            <!-- ... other inject declarations -->
        </injects> 
    </factory>   
    
    <properties>   <!-- properties declaration -->
        <configure ... > <!-- component configuration declaration --> 
            <property ... /> <!-- single value property declaration -->
            <property ...> <!-- multi value property (vector) declaration -->
                <value>...</value>
                <!-- ... other value declarations -->
            </property> 
            <!-- ... other property declarations -->               
        </configure>      
        <!-- ... other component configuration declarations -->              
    </properties>   
</xpcf-registry>

Node description

Section (parent node) Node Attributes Semantic
document root xpcf-registry autoAlias = [true, false]
-> set whether XPCF must automatically create aliases while parsing the file between :
component UUID <-> component name
interface UUID <-> interface name
This node declares an xpcf registry file, containing both components and modules structure and their properties
document root xpcf-configuration This node declares an xpcf configuration file only, containing components properties
xpcf-registry module uuid = module is referenced with an uuid. It must be declared here.
name = name of the module used
path = path to the module used (can contain environment variables)
description = the module function description
declares an xpcf module
module component uuid = component is referenced with an uuid. It must be declared here.
name = name of the component used
description = the component description
declares an xpcf component inside a module
component interface uuid = interface is referenced with an uuid. It must be declared here.
name = name of the interface used
description = the interface description
declares an xpcf interface implemented by a component
xpcf-registry / xpcf-configuration aliases declares an aliases section. When autoAlias = true in <xpcf-registry>, an alias is created for each component/interface in <module> node when the first occurence of a name is met. Hence the <aliases> section must be used to resolve any name ambiguity, or to simplify a non intuitive name.
aliases alias name = name of the alias
type=[component, interface]
uuid = the uuid of the component/interface targeted by the alias
declares an alias. Alias can be ...
xpcf-registry / xpcf-configuration factory declares the factory section
factory bindings declares the bindings section - this section is needed only to overload autobinds made while parsing the <module> node
bindings bind interface = interface alias or uuid
to = component alias or uuid
[optional] name = name of the binding
[optional] range = [all, default, named, withparents, explicit] the binding range
[optional] scope = [transient, singleton] the binding scope: singleton ensure there will be only one instance of the binded component in this factory context. Transient scope ensure a component is created for each resolution made within the factory context.
[optional] properties = name - the properties name to use to initialize the binded component
declares a default or named bind between an interface and a component
factory injects declares the injects section - used for structured (also called planned) injection for specific component class
injects inject to = component alias or uuid declares an injection pattern for a specific component class
inject bind interface = interface alias or uuid
to = component alias or uuid
[optional] name = name of the binding
[optional] range = [all, default, named, withparents, explicit] the binding range
[optional] scope = [transient, singleton] the binding scope: singleton ensure there will be only one instance of the binded component in this factory context. Transient scope ensure a component is created for each resolution made within the factory context.
[optional] properties = name - the properties name to use to initialize the binded component
declares a specific bind (component context bind) between an interface and a component for the specific component class declared in <inject>
xpcf-registry / xpcf-configuration properties declares the components properties section. This section defines component parameters values for configurable components.
properties configure [optional] uuid = the component uuid or
component = a component alias name
declares the configuration for a specific component
configure property name = property name
type = [int, uint, long, ulong, string, wstring, float, double, structure]
value = property value (must be a valid value for the type chosen)
value can also be declared as sub-nodes for vector properties
declares a component property
property value declares a component property value

Note :

Aliasing order : an alias declared in the section is added to the list of existing aliases, or replace any existing alias with the same name. section overload autoAlias already declared.

Binding order :

  • autobind : during the parsing of the <module> nodes, a bind is added the first time an interface is met. If the same interface is also declared for another component, the first bind is maintained. where a [interface, component] pair is met.
  • factory bindings : when an explicit bind is defined, it replaces any previous autobind made while parsing the <module> nodes.

Sample configuration file

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<xpcf-registry autoAlias="true">
    <module uuid="{3b899ff0-e098-4218-bdaa-82abdec22960}" name="xpcfModule" description="an sample module with some components" path="$REMAKEN_RULES_ROOT/xpcfModule/2.3.1/lib/x86_64/shared">
	    <component uuid="ae9d10bb-4bd1-462b-8c01-43f1815c6ae0" name="feature1Component - not configurable" description="feature1Component is not Configurable, it implements I0 and I1 interfaces">
	        <interface uuid="125f2007-1bf9-421d-9367-fbdc1210d006" name="XPCF::IComponentIntrospect" description="Component introspection interface."/>
	        <interface uuid="46333fd5-9eeb-4c9a-9912-b7df96ccf5fc" name="I0" description="provides I0 features"/>
	        <interface uuid="3bc8f5ea-ee24-473e-8afd-4f5b1b21c018" name="I1" description="provides I1 features"/>
	    </component>
	    <component uuid="63ff193d-93e6-4ede-9947-22f864ac843f" name="feature2Component - configurable" description="feature2Component implements IConfigurable, I2 and I1 interfaces">
	        <interface uuid="125f2007-1bf9-421d-9367-fbdc1210d006" name="XPCF::IComponentIntrospect" description="Component introspection interface."/>
	        <interface uuid="98dba14f-6ef9-462e-a387-34756b4cba80" name="XPCF::IConfigurable" description="Provides generic configuration ability to any component through metadata parameters"/>
	        <interface uuid="3bc8f5ea-ee24-473e-8afd-4f5b1b21c018" name="I1" description="provides I1 features"/>
	        <interface uuid="41348765-1017-47a7-ab9f-6b59d39e4b95" name="I2" description="provides I2 features"/>
	    </component>
	    <component uuid="{7cc64f89-9a81-4fc8-9695-fde123fae225}" name="technicalComponent - not configurable" description="component provides technical feature to functional components">
	        <interface uuid="125f2007-1bf9-421d-9367-fbdc1210d006" name="XPCF::IComponentIntrospect" description="Component introspection interface."/>
	        <interface uuid="41348765-1017-47a7-ab9f-6b59d39e4b95" name="I2" description="provides I2 features"/>
	        <interface uuid="6279bb1d-69b5-42bd-9da1-743c8922bcb9" name="I3" description="provides I3 features"/>
	    </component>
    </module>
    <aliases>
            <alias name="feature1Component" type="component" uuid="ae9d10bb-4bd1-462b-8c01-43f1815c6ae0"/>
            <alias name="feature2Component" type="component" uuid="63ff193d-93e6-4ede-9947-22f864ac843f"/>
            <alias name="technicalComponent" type="component" uuid="7cc64f89-9a81-4fc8-9695-fde123fae225"/>
    </aliases>
    <factory>
        <!-- default bindings -->
        <bindings>
            <bind interface="I1" to="feature1Component" />
            <bind interface="3bc8f5ea-ee24-473e-8afd-4f5b1b21c018" to="ae9d10bb-4bd1-462b-8c01-43f1815c6ae0" />
            <bind name="feature2Component_I1" interface="I1" to="feature2Component"/>
        </bindings>
        <!-- custom bindings : explicit injection for a particular component class -->
        <injects>
            <inject to="feature1Component">
                    <bind interface="I2" to="technicalComponent" />
            </inject>
        </injects>
    </factory>
    <properties>
        <configure component="feature2Component">
                <property name="parameter1" type="double" value="2.0"/>
                <property name="vector_parameter1" type="double" access="ro">
                    <value>0.2</value>
                    <value>0.5</value>
                </property>
                <property name="structured_parameter"  type = "structure">
                    <property name="cols" type="long" value="2"/>
                    <property name="rows" type="long" value="2"/><!-- rowmajormode only-->
                    <property name="values" type="double">
                        <value>1.1</value>
                        <value>0.7</value>
                        <value>0.3</value>
                        <value>2.7</value>
                    </property>
                </property>
        </configure>
    </properties>
</xpcf-registry>

How to build XPCF

In order to build XPCF:

  • Install conan
  • Install QT to get QT Creator
  • Clone xpcf repository with submodules (git clone --recursive https://github.com/b-com-software-basis/xpcf.git). If you have already cloned the repository but not the submodules, run git submodule update --init --recursive within your xpcf repository folder.
  • Open xpcf.pro file in QT Creator and configure the project with the compilation kit of your OS (gcc on Linux, clang on mac os X, msvc 2017 for windows)
  • Run qmake - this can take a while as it should compile boost from conan.
  • Add an install target for make in "compilation steps" from the "projects" tab in Qt Creator

You can now build xpcf using Build inside Qt Creator.

The compilation result will be located in $HOME/.remaken/packages/[platform-compiler-version]/xpcf/XPCF\_VERSION (%USERPROFILE%\\.remaken\packages\\[platform-compiler-version]\xpcf\XPCF\_VERSION on windows)

Note: To automatically include and link with xpcf headers and library, use the pkgconfig file bcom-xpcf.pc located at the root of the above folder.

Creating your first component

Components are hosted in modules (i.e. shared libraries with special hook methods). Hence prior to the creation of components, start by creating a module.

To create an XPCF module, interface or component it is recommended to use the QT Creator wizards located in wizards/qtcreator folder in xpcf repository.

To install the wizards, please refer to INSTALL.txt.

xpcf's People

Contributors

adrienschadle avatar firefly35 avatar granger35 avatar stefled avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

xpcf's Issues

InjectableNotFoudException when using mngr->createComponent()->bindTo()

HOW TO REPRODUCE:
In configuration file declare a component like this:

<!-- top level module-->
<module uuid="<uuid module>" name="Module" [...]>
    <component uuid="<uuid A>" name="A">
        <interface uuid="125f2007-1bf9-421d-9367-fbdc1210d006" name="IComponentIntrospect" description="IComponentIntrospect"/>
        <interface uuid="<uuid IA>" name="IA"/>
    </component>
</module>
[...]
<module>
  <!-- component used by top level module -->
  <component uuid="<uuid B>" name="B" >
      <interface uuid="125f2007-1bf9-421d-9367-fbdc1210d006" name="IComponentIntrospect" description="IComponentIntrospect"/>
      <interface uuid="<uuid IB>" name="IB"/>
  </component>
<module>
[...]
<bindings>
    <bind interface="IB" to="B" scope="Singleton"/>
</bindings>

Resolve top level component with this code:

auto manager= xpcf::getComponentManagerInstance();
manager->load(conf_file_path);
component = manager->createComponent<IA>(xpcf::toUUID(<uuid A>))->bindTo<IA>();

EXPECTED BEHAVIOR
component is created

OBSERVED BEHAVIOR
InjectableNotFoudException is thrown
No [auto|default|named] binding found to resolve component from interface UUID = <uuid IB>

COMMENTS
In case this is hard to reproduce, you can try to use the SolAR Unity plugin
https://github.com/SolarFramework/SolARUnityPlugin

With PipelineManager submodule at this commit:
SolarFramework/SolARPipelineManager@b00e5c3

(do not take a more recent commit because a fix will be uploaded, so the bug won't be reproducible)

And try to start the SLAM pipeline sample from Unity.

This behavior is not observed if component is instead created with the following method:
component= manager->resolve<IA>();
Here, the component is created with no error.

[GRPC_GEN] generated packagedependencies.txt is erroneous

HOW TO REPRODUCE
Use it on the SolAR framework interfaces with a command line like:

~/.remaken/packages/linux-gcc/xpcf_grpc_gen/2.6.2/bin/x86_64/shared/release/xpcf_grpc_gen  --module_uuid a0f522d1-b70e-4d0f-ad78-84e78a9af6bf -v 1.0.0 -r SolARbuild@github -u https://github.com/SolarFramework/SolARFramework --database_dir /home/user/work/SolAR/core/build-SolARFramework-Desktop_Qt_5_15_2_GCC_64bit-Release/ --std c++1z --remove_comments_in_macro -g protobuf -o ~/work/SolAR/core/SolARFrameworkGRPCRemote/

EXPECTED BEHAVIOR
In the output dir, which is the directory of the SolARGRPCFrameworkRemote project in the example, packagedependencies.txt stays the same and is OK, i.e.:

xpcf|2.6.2|xpcf|@github|https://github.com/b-com-software-basis/xpcf/releases/download|shared|
SolARFramework|1.0.0|SolARFramework|SolARBuild@github|https://github.com/SolarFramework/SolARFramework/releases/download

OBSERVED BEHAVIOR
File packagedependencies.txt is overwritten and becomes

xpcf|2.6.2|xpcf|@github|https://github.com/SolarFramework/binaries/releases/download

Which is wrong, since XPCF 2.6.2 is not present at this location.

XPCF-gRPC-gen does not seem to support -isystem

HOW TO REPRODUCE

  1. Get project and generate a compilation database that contains -isystem
  • git clone https://github.com/SolarFramework/SolARFramework.git -b develop && cd SolARFramework && git checkout be919630e0b6d1192f4278707bdf78453a4ec48e
  • Open QtCreator and open SolARFramework.pro
  • Go to projects tab, and select run for qmake_system()
  • Build project and then generate compilation database: Build -> Run Generator -> Generate Compilation Database
  • The location of the generated compilation database should be indicated in the Genera Messages window. This should be something like ../build-SolARFramework-Desktop_Qt_6_5_3_GCC_64bit-Release/compile_commands.json

The compilation database should be ill-formed as long as the issue b-com-software-basis/builddefs-qmake#13 is not resolved.
To reproduce this bug, you must manually alter the JSON file by replacing -I-isystem/ string by -system/.

  1. Get project that uses XPCF-gRPC-gen on the previous project.
  • git clone https://github.com/SolarFramework/SolARFrameworkGRPCRemote.git -b develop && cd SolARFrameworkGRPCRemote && git checkout 8ca88680ec599d97d3d4d0c51cd69bb5072689ca
  • run generate.sh

EXPECTED BEHAVIOR
Script exits with code 0.

OBSERVED BEHAVIOR

[simple file parser] [info] parsing file '/home/user/dev/SolAR/core/SolARFramework/src/api/reloc/IKeyframeRetriever.cpp'
[preprocessor] [error] In file included from /home/user/dev/SolAR/core/SolARFramework/src/api/reloc/IKeyframeRetriever.cpp: 17:
[preprocessor] [error] In file included from /home/user/dev/SolAR/core/SolARFramework/interfaces/api/reloc/IKeyframeRetriever.h: 20:
[preprocessor] [error] In file included from /home/user/dev/SolAR/core/SolARFramework/interfaces/datastructure/Keyframe.h: 22:
[preprocessor] [critical] /home/user/dev/SolAR/core/SolARFramework/interfaces/datastructure/GeometryDefinitions.h:22: 'xpcf/xpcf.h' file not found

NOTES

  • XPCF headers directory is specified by the compilation database as
"-isystem/home/jmhenaff/.remaken/packages/linux-gcc/xpcf/2.7.0/interfaces",

after the manual find'n replace that removes the erroneous -I-isystem/ part.

  • If all occurences of -isystem are replaced by -I, the project compiles successfully.

OS: Linux Ubuntu 22.04
XPCF: 2.7.0 (static)
remaken: 1.10.0
Qt Creator: 11.0.2
Qt: 6.4.3

Repository management good practices

  • avoid unexplicit merge commits on master (Merge develop is not an explicit commit). Merge commits on master must contain the synthesis of the changelog occured between commits on master
  • update CHANGELOG.md to reflect this information (not only update the xpcf version). MAINTAIN THE CHANGELOG.
  • latest commit[a3a1ee6] removes exception handling/rewrapping: exception handling and error handling were made ON PURPOSE. Exceptions are wrapped in more specialized exceptions at will, removing the wrapping prevent to categorize exceptions from the user point of view (see Factory removal of such wrapping). Moreover no documentation about newly raised exceptions is added in the XPCF concerned headers. Update documentation in headers accordingly.
  • Commits messages must reflect the scope of their changes: for instance ef13dc9 "fix(update): grpc generators update" not only modifies the grpc generator, but also changes the exception handling in xpcf factory which is sensitive. There is no relation between the commit message and the exception handling/rewrapping in xpcf Factory.

Maybe there must be a reviewing process before anything is merged in master with adequate people to validate changes.

[GRPC_GEN] Path in #include of generated files are ill-formed (either absolute path or relative path from home dir)

HOW TO REPRODUCE
Use it on the SolAR framework interfaces with a command line like:

~/.remaken/packages/linux-gcc/xpcf_grpc_gen/2.6.2/bin/x86_64/shared/release/xpcf_grpc_gen  --module_uuid a0f522d1-b70e-4d0f-ad78-84e78a9af6bf -v 1.0.0 -r SolARbuild@github -u https://github.com/SolarFramework/SolARFramework --database_dir /home/user/work/SolAR/core/build-SolARFramework-Desktop_Qt_5_15_2_GCC_64bit-Release/ --std c++1z --remove_comments_in_macro -g protobuf -o ~/work/SolAR/core/SolARFrameworkGRPCRemote/

EXPECTED BEHAVIOR
Project files are generated correctly and program compiles.

OBSERVED BEHAVIOR
Path in #include are too long, sometimes absolute, sometimes from home dir (this is the case with the previous command line, where path to interfaces is expressed from ~):

#include "work/SolAR/core/SolARFramework/interfaces/api/display/I2DOverlay.h"

instead of

#include "api/display/I2DOverlay.h"

IIRC, generated path are absolute if path to framework is expressed relatively (../[...]) or absolutely.

OS: linux Ubuntun 18.04
xpcf_grpc_gen 1.6.2
SolAR Framework: develop branch (1.0.0)

Build scripts not working anymore on master since august

I recently cloned xpcf and tried to build it on MacOs with the build_xpcf.sh script within scripts/unixes.
The build script doesn't work anymore when simply launching ./build_xpcf.sh leading to "Project root path '5.15.2' doesn't exist".
The scripts seem to be broken since the d9404af commit (august 26), when the qmake_path parameter was added at the top level, but not correctly reflected in the underlying script build_remaken_project.sh where the QMAKE parameter was added in the middle of the parameters list.

My advices:

  1. the new parameter must have a default value in the top level script
  2. the new parameter should have been added at the end of the parameters list, to preserve backward compatibility and behavior
  3. the top level build_xpcf.sh script must forward correctly the parameter to the underlying scripts
  4. the usage documentation in build_remaken_project.sh must reflect the added parameter
  5. To prevent from breaking working behavior on the master branch, someone should be in charge to lead the Software Quality of the product. Maybe going for systematic peer review before any push/merge on the master branch could prevent from frequent fixes/revert and time waste for successive erroneous commits.

[Feature Request] Add automatic enum parsing in configuration files

According to the documentation, currently supported types for properties are:

[int, uint, long, ulong, string, wstring, float, double, structure]

I don't know if this is possible, but it would be nice to be able to handle enum types, so that all the required checks are performed by XPCF instead of doing this kind of things in every components:

  • Get the enum value as a string in the configuration file
  • Maintain an associative container / add a method that performs a switch to map the string value to the enum value and check the input string is among the valid values of the enum.

Maybe something like this MIT-licensed library could be used ?
https://github.com/Neargye/magic_enum

It can get the enum value out of a string

std::string color_name{"GREEN"};
auto color = magic_enum::enum_cast<Color>(color_name);
if (color.has_value()) {
  // color.value() -> Color::GREEN
}

With XPCF, a component declaring a property could then look like this

enum class Orientation { Up, Down};
...
Orientation m_orientation;
...
declareProperty<Orientation>("orientation", m_orientation); // raises a configuration error if the string value cannot be converted to an Orientation

[Linux] Qt Wizard for XPCF component ignores case and produces only lower-case names

HOW TO REPRODUCE

  • run install.sh to install wizards
  • open an existing module project in Qt
  • right click on Sources, select add New -> XPCF -> XPCF Component class
  • give it name with upper and lower case letters (e.g. MyComponent) (+ fill other fields according to documentation)
  • click next to the Summary page and click Finish

EXPECTED BEHAVIOR
Summary displays file to be added as MyComponent.cpp and MyComponent.h
After Finish is clicked, these files are located in Sources folder.

OBSERVED BEHAVIOR
File names in Summary and files actually created only have lowercase characters in their names (mycomponent.cpp, mycomponent.h)

OS: Linux
XPCF: v 2.5.0
QtCreator: v5.0.2
Qt: v5.15.2

Components injection

How to inject two same components with different names in another component? For example, I would like to create ComponentA that injects two ComponentB as follows:
declareInjectable<InterfaceComponentB>(componentB1); declareInjectable<InterfaceComponentB>(componentB2, "B2");
And my configuration file:
<factory> <bindings> <bind interface="InterfaceComponentB" to="ComponentB""/> </bindings> <injects> <inject to="ComponentA"> <bind interface="InterfaceComponentB" to="ComponentB" name="B2"/> </inject> </injects> </factory>
Then I run my program to create ComponentA but it crashes when injecting the second ComponentB.
I found a not clear solution to resolve this problem by moving the bind of the second ComponentB in the bindings as follows:
<factory> <bindings> <bind interface="InterfaceComponentB" to="ComponentB""/> <bind interface="InterfaceComponentB" to="ComponentB" name="B2"/> </bindings> <injects> <inject to="ComponentA"> </inject> </injects> </factory>

Return more feedback in case of property issue

When a property is not well set in the registery file (property does not exist, wrong type, no value or null/empty value, wrong value format such as a float with a coma, etc.) could it be possible to add an explicit message when the exception is thrown ?

ComponentBase::addInterface() deprecation comment + assert message

As I was removing warnings in our code base, I noticed one about ComponentBase::addInterface() being deprecated.
I had to look into the code to determine by what I could replace my call: addInterface() calls declareInterface().

  • Could you add a comment in the attribute (e.g. [[deprecated("Use declareInterface() instead")]]) ? The method could also be removed but maybe you prefer to wait for a new major release to break this API ?

  • Also, the assert messages in declareInterface() are not up-to-date:

"Type passed to addInterface is not an interface "
[...]
"Interface type passed to addInterface is not a derived class of IComponentIntrospect !!"

Either use the new declareInterface() name or both, until addInterace() is removed ?

Error message when component is missing in module is not clear (boost assertion failure)

HOW TO REPRODUCE

  • Write a project configuration file with a module listing a non existant component

I don't know if it important but, my project had also this:

  • Add a factory/bindings/bind element with a name for "properties"
  • Add a properties/configure section with some properties
  • resolve in application with:
result= xpcfCompMngr->resolve<MyNonExistant::Component::Interface>("<binding name>");

EXPECTED BEHAVIOR
An error message like:

Error: could not find component '<UUID and/or binding name and/or component name>' in module '<module UUID and/or module name> at <path to resolved module>'

Or anything else that allows to understand the component is missing, and the error has been handled.

OBSERVED BEHAVIOR

<executable name>: /home/jmhenaff/.conan/data/boost/1.74.0/_/_/package/9cb93833acae02c84075e30719a252a53c8b6ca7/include/boost/smart_ptr/shared_ptr.hpp:728: typename boost::detail::sp_member_access<T>::type boost::shared_ptr<T>::operator->() const [with T = org::bcom::xpcf::IComponentIntrospect; typename boost::detail::sp_member_access<T>::type = org::bcom::xpcf::IComponentIntrospect*]: Assertion `px != 0' failed.

And nothing else (also no xpcf::Exception seems to have been thrown)

Sorry I don't have an exact repro case (out of time, I just wanted to leave a trace here), let me know if you have some difficulties to reproduce it.

Thanks.

OS: Linux
XPCF version: 2.5.0
Configuration: debug

Add .gitignore

Is it possible to add a .gitignore to the project root?

At least for :

  • build
  • *.pro.user

[FR] Revive xpcf cli project for module instrospection

PROBLEM
Currently our configuration files tend to contain a comprehensive list of all available properties for all the components that are used.

This serves somehow as a documentation for the user, letting him know what are the available properties with their types.

This has a significant maintenance cost when the module components evolves (add/remove/rename properties).

But this is the only way for us to let the user know about the properties in a convenient manner, apart from documenting them in the component header file (which we do), or in a textual documentation shipped with the component.

All these solutions have the same maintenance issue.

SUGGESTED SOLUTIONS
It'd be nice if we could have only one source of truth, the source code.

It appears that the xpcfcli project does just that.

This could allow us to reduce the size of our configuration files, and give a user a way to update its configuration file without having access to the source (since the definition of the module properties is done in the .cpp file).

I gave it a try on on of our modules, but it crashed after having partially produced an xml output of the component list:

xpcfcli: ../../.conan/data/boost/1.78.0/_/_/package/9c097173c769786802e85151204758f1f62f461f/include/boost/smart_ptr/shared_ptr.hpp:786: typename boost::detail::sp_member_access<T>::type boost::shared_ptr<T>::operator->() const [with T = org::bcom::xpcf::IPropertyManager; typename boost::detail::sp_member_access<T>::type = org::bcom::xpcf::IPropertyManager*]: Assertion `px != 0' failed.
Aborted

So I guess this project might need an update.

We coud use it to dump a configuration file template for the all modules for example, filter components by name to get a template with only the one we intend to use in a given app, or find other usage to be determine.

Access value of property of type size_t via getUnsignedIntegerValue() always return 0

In class A, we defined a member variable m_prop as follows:
class A {
size_t m_prop;
}
and in constructor of class A,
A::A():ConfigurableBase(xpcf::toUUID())
{
declareProperty("prop", m_prop);
}

Then in class B, we had an object of class A named as a.
When we do a->bindToxpcf::IConfigurable()->getProperty("prop")->getUnsignedIntegerValue(), it always returned 0, whatever the value of "prop" we put in the xml file.

However, when we replace size_t by uint32_t, everything works, we get the expected property value.
class A {
uint32_t m_prop;
}

I think maybe it is related to the implementation below:

virtual uint32_t getUnsignedIntegerValue (uint32_t itemIndex = 0) const = 0;

It would suggest to log an error message, instead of return 0, when getUnsignedIntegerValue() is called to access the value of a property of type size_t ?

Thanks in advance

[Feature Request] Make XPCF_IGNORE the default regarding generation of gRPC clients and services

PROBLEM

  • Our project contains quite a lot of XPCF components subject to have gRPC files being generated by xpcf_grpc_gen.
  • We want to generate those files only for a subset of these classes
  • We must use XPCF_IGNORE macro for a majority of component and keep XPCF_CLIENTUUID/XPCF_SERVERUUID only for a handful of classes, because, IIUC, unannotated class uses generated UUIDs :

ProxyGenerator.cpp

xpcf::uuids::uuid proxyUUID = c->getClientUUID();
    if (proxyUUID.is_nil()) {
        proxyUUID = c->createClientUUID();
        m_serviceUuidMap[m_className] = xpcf::uuids::to_string(proxyUUID);
    }
  • => this can be tedious and hard to check we haven't forgotten to add an XPCF_IGNORE somewhere as it will likely generate files we do not use, and only increase compilation time to our project that uses those generated files.

SUGGESTED SOLUTION

  • Invert the default, and consider an unannotated class to be ignored, and explicitely add annotation for generation.

For example:

  • set both UUIDs
class XPCF_CLIENTUUID("...") XPCF_SERVERUUID("...") MyClass:
    virtual public org::bcom::xpcf::IComponentIntrospect {
  • set only one UUID, let the other one be generated (error if not both are set?)
class XPCF_CLIENTUUID("...") XPCF_SERVERUUID_AUTO MyClass:
    virtual public org::bcom::xpcf::IComponentIntrospect {
  • no annotation -> ignored
class MyClass:
    virtual public org::bcom::xpcf::IComponentIntrospect {

[Wizards] Generate use of XPCF_DEFINE_FACTORY_CREATE_INSTANCE() macro

After creating an XPCF component with the Wizard, at the top the cpp file, there's this statement:

template<> My::Component::Class * xpcf::ComponentFactory::createInstance<My::Component::Class>();

By looking at other components I saw that the equivalent of this call was made more compact with the use of a macro, e.g.:

XPCF_DEFINE_FACTORY_CREATE_INSTANCE(My::Component::Class)

If this is the recommended way to do it, it could be nice to make the wizard generate the call to the macro, in addition this leads to smaller boilerplate code.

Allow disabling definition of custom attributes

PROBLEM

  • In an attempt to reduce warnings in SolARFramework, we encountered several warnings emitted by GCC for each reference to a non-standard attribute to say it will ignore it.
    Ex:
warning: ‘xpcf::clientUUID’ scoped attribute directive ignored [-Wattributes]
  • Unfortunately, there is no support for GCC to filter attributes to ignore.
    Here's the documentation for GCC 7 (the one we currently use):
-Wno-attributes
Do not warn if an unexpected __attribute__ is used, such as unrecognized attributes, function attributes applied to variables, etc. This does not stop errors for incorrect use of supported attributes.
  • We could use this (in fact, I think we'll use that as a best effort option), but the trouble is that it will silence too much.
    For exemple, a typo when writing the name of a known attribute such as [[maybe_unused]] (but in this case this will probably emit a warning about an unused parameter (for example), so it won't stay unnoticed).

In GCC12, it becomes possible to specify such filter (doc):

-Wno-attributes=xpcf::clientUUID

But we're not going to be using GCC12/13 anytime soon.

SUGGESTED SOLUTION

  • Currently, XPCF wraps a set of attributes using macros in helpers.h. The definition of theses attributes can be set to nothing thanks the the SWIG flag.
  • We could use such a mechanism with another flag (since it's not related to SWIG) so that we can get rid of the warnings by removing the use of these attributes, since the gRPC attributes are not necessary for all of our projects, but only for the one that uses XPCF to generates gRPC clients and services.
  • I've made a test locally with this flag: XPCF_DISABLE_ATTRIBUTES
    and this change to helpers.h:
#if !defined(SWIG) && !defined(XPCF_DISABLE_ATTRIBUTES)

NOTES

  • I'm openning this as a discussion. I'm afraid it would introduce changes that are more related to our project than to XPCF.
  • If you think this need might be generalized, we could discussed a way to do it properly, otherwise, it might not be worth integrating this change as a minor workaround specific to our project.

[xpcf_grpc_gen] Behavior differs for same types according to whether or not --file argument is used

HOW TO REPRODUCE

  • Generate SolARFrameworkGRPCRemote with script, i.e. in a mode where the SolARFramework compilation database is generated. Command should be
xpcf_grpc_gen \
   --module_uuid <...> \
   --name SolARFramework \
   --project_version <...> \
   --repository SolARBuild@github \
   --url https://github.com/SolarFramework/SolARFramework/releases/download \
   --database_dir <path to compilation db dir> \
   --std c++1z \
   --remove_comments_in_macro \
   --generator protobuf\
   --interfaces_folder ../SolARFramework/interfaces/ \
   --output gen
  • Compare with génération after parsing of single file IARDevice.h
xpcf_grpc_gen \
   --module_uuid <...> \
   --name SolARFramework \
   --project_version <...> \
   --repository SolARBuild@github \
   --url https://github.com/SolarFramework/SolARFramework/releases/download \
   --database_dir <path to compilation db dir> \
   --database_file <path to compilation db dir>/compile_commands.json \
   --file ../SolARFramework/interfaces/api/input/devices/IARDevice.h \
   --std c++1z --remove_comments_in_macro \
   --generator protobuf \
   --interfaces_folder ../SolARFramework/interfaces/ \
   --output gen

EXPECTED BEHAVIOR

  • both commands generate a proto, and then gRPC service/clients for IARDevice

OBSERVED BEHAVIOR

  • Only the generation from the complete database produces a result. When invoking xpcf_grpc_gen with a single file, no proto is generated and thus no gRPC clients/servers.

Attached to this issue, you'll find the complete log of the --file version as well as the matching section in the complete database version log.

Here's the diff:

1c1
< AST for '/home/jmhenaff/dev/SolAR/core/SolARFramework/interfaces/api/input/devices/IARDevice.h':
---
> AST for '../SolARFramework/interfaces/api/input/devices/IARDevice.h':
31,45d30
< parseMethods::Base name=org::bcom::xpcf::IComponentIntrospect
<  ==> parsing member function start ()
< => constness [ not const ]
<  => type [ SolAR::FrameworkReturnCode ]
<  => type kind [  ]
<  => derefed type [ Enum type ]
< SolAR::FrameworkReturnCode
<  ==> Method declaration start()
<  ==> parsing member function stop ()
< => constness [ not const ]
<  => type [ SolAR::FrameworkReturnCode ]
<  => type kind [  ]
<  => derefed type [ Enum type ]
< SolAR::FrameworkReturnCode
<  ==> Method declaration stop()
83d67
< Entity Transform3Df is a type alias
102c86
<  => derefed type [ Enum type ]
---
>  => derefed type [ User defined type ]
119a104
> Found user defined type = SolAR::api::input::devices::IARDevice
157c142,147
< : `static constexpr char const* const DESCRIPTION="SolAR::IARDevice interface";`
\ No newline at end of file
---
> : `static constexpr char const* const DESCRIPTION="SolAR::IARDevice interface";`
> Searching protobuf compiler (protoc) from path: 
> ===> /home/jmhenaff/.conan/data/protobuf/3.21.9/_/_/package/774d45d697ce6041148f1cc945854892effde458/bin/
> Using protobuf compiler (protoc) from path: 
> ===> /home/jmhenaff/.conan/data/protobuf/3.21.9/_/_/package/774d45d697ce6041148f1cc945854892effde458/bin/
> m_protoNameFilesPathMap.size()=0

NOTES
Also other part of code are generated differently, for instance:

return static_cast<SolAR::FrameworkReturnCode>(respOut.xpcfgrpcreturnvalue());

With the DB, becomes, with only one file:

return xpcf::deserialize<SolAR::FrameworkReturnCode>(respOut.xpcfgrpcreturnvalue());

IARDeviceParse-db.txt
IARDeviceParse-file.txt

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.