CommonAPI学习笔记-2

一. 概述

​ 这篇文章主要是想整理并且分析CommonAPI代码生成工具根据fidl和fdepl配置文件生成出来的代码的结构和作用。

二. fidl

​ 用户根据业务需求在fidl文件中定义业务服务接口的结构以及自定义数据类型,然后使用core生成工具传入fidl文件生成该fidl的核心层Proxy和Stub以及配套的相关代码。

​ 例如官方范例上的HelloWorld(https://github.com/COVESA/capicxx-core-tools/blob/master/CommonAPI-Examples/E01HelloWorld/fidl/E01HelloWorld.fidl),使用生成工具commonapi-core-generator-linux-x86_64输入该fidl文件可以生成HelloWorld.hpp,HelloWorldProxy.hpp,HelloWorldProxyBase.hpp,HelloWorldStub.hpp,HelloWorldStubDefault.hpp这5个代码文件。

2.1 interface

​ 一个fidl文件对应一个CommonAPI的interface,对于C++来说,要定义一个interface,当然是使用一个抽象类来定义了,这个抽象类就是HelloWorld.hpp文件中的HelloWorld类了。

namespace v1 {
namespace commonapi {

class HelloWorld {
public:
    virtual ~HelloWorld() { }

    static inline const char* getInterface();                  // HelloWorld业务的CommonAPI接口名称
    static inline CommonAPI::Version getInterfaceVersion();    // HelloWorld业务的CommonAPI接口版本
};

const char* HelloWorld::getInterface() {
    return ("commonapi.HelloWorld:v1_0");
}

CommonAPI::Version HelloWorld::getInterfaceVersion() {
    return CommonAPI::Version(1, 0);
}


} // namespace commonapi
} // namespace v1

​ 当看到core工具生成的HelloWorld接口类,感觉有点奇怪,这个HelloWorld接口类只定义并且实现了两个关于接口信息的函数(接口名称和接口版本信息),而fidl中关于HelloWorld接口中的业务函数sayHello在生成的HelloWorld接口类中确没有

// HelloWorld.fidl
package commonapi

interface HelloWorld {
  version {major 1 minor 0}
  method sayHello {    // 该函数没有生成在HelloWorld接口类中
    in {
      String name
    }
    out {
      String message
    }
  }
}

​ 对于为什么生成的HelloWorld接口类中没有定义sayHello函数的原因,个人阅读完生成的代码后理解是,由于HelloWorld.fidl生成的核心层Proxy以及Stub代码在sayHello函数上的定义是不兼容的,而HelloWorld接口类又是核心层HelloWorldProxy和HelloWorldStub的父类,因此无法将sayHello函数的申明放入HelloWorld接口类中。

​ HelloWorld接口类中的getInterface和getInterfaceVersion函数是为了给CommonAPI核心层代码库创建核心层Proxy/Stub的时候使用的,代码如下:

// 代码文件/usr/local/include/CommonAPI-3.2/CommonAPI/Runtime.hpp
template<template<typename ...> class ProxyClass_, typename ... AttributeExtensions_>
    COMMONAPI_EXPORT std::shared_ptr<
        ProxyClass_<AttributeExtensions_...>
    >
    buildProxy(const std::string &_domain,
               const std::string &_instance,
               const ConnectionId_t &_connectionId = DEFAULT_CONNECTION_ID) {
        std::shared_ptr<Proxy> proxy
            = createProxy(_domain,
                          //这里的ProxyClass就是HelloWorldProxy类,调用HelloWorldProxy的父类HelloWorld中的getInterface
                          ProxyClass_<AttributeExtensions_...>::getInterface(),   
                          _instance,
                          _connectionId);

        if (proxy) {
            return std::make_shared<ProxyClass_<AttributeExtensions_...>>(proxy);
        }
        return nullptr;
    }

2.2 Proxy

​ 在核心层生成了HelloWorldProxy.hpp和HelloWorldProxyBase.hpp两个代码文件,里面分别定义了HelloWorldProxy和HelloWorldProxyBase两个类。

​ 其中,HelloWorldProxyBase是HelloWorldProxy的一个父类,其中包含了fidl中sayHello函数的相关定义(包括了同步调用和异步调用两种)

class HelloWorldProxyBase
    : virtual public CommonAPI::Proxy {
public:

    typedef std::function<void(const CommonAPI::CallStatus&, const std::string&)> SayHelloAsyncCallback; // 异步调用的回调通知接口

    virtual void sayHello(std::string _name, CommonAPI::CallStatus &_internalCallStatus, std::string &_message, const CommonAPI::CallInfo *_info = nullptr) = 0;  // 同步调用接口
    virtual std::future<CommonAPI::CallStatus> sayHelloAsync(const std::string &_name, SayHelloAsyncCallback _callback = nullptr, const CommonAPI::CallInfo *_info = nullptr) = 0;  // 异步调用接口

    virtual std::future<void> getCompletionFuture() = 0;
};

HelloWorldProxy类中实现了HelloWorldProxyBase父类中定义的sayHello相关接口:

template <typename ... _AttributeExtensions>
void HelloWorldProxy<_AttributeExtensions...>::sayHello(std::string _name, CommonAPI::CallStatus &_internalCallStatus, std::string &_message, const CommonAPI::CallInfo *_info) {
    delegate_->sayHello(_name, _internalCallStatus, _message, _info);
}

​ 可以看到,HelloWorldProxy内部实现fidl中定义的接口时,还依赖了一个delegate_对象,这个delegatge_对象是HelloWorldProxyBase类型的共享指针

template <typename ... _AttributeExtensions>
class HelloWorldProxy
    : virtual public HelloWorld,
      virtual public HelloWorldProxyBase,
      virtual public _AttributeExtensions... {
 public:
	...
 private:
    std::shared_ptr< HelloWorldProxyBase> delegate_;
};

​ 这里就有点不对了,前面说过HelloWorldProxy的一个父类就是HelloWorldProxyBase,难道HelloWorldProxy对象内部的这个delegate_共享指针是指向自己的?当然不是,在src-gen目录下查找还有没有其他类是HelloWorldProxy,结果看到HelloWorldProxyBase类也是HelloWorldSomeIPProxy类的父类,HelloWorldSomeIPProxy是通过commonapi-someip-generator-linux-x86_64工具生成的绑定层的Proxy类。

​ 也就是说核心层的Proxy是依赖绑定层的Proxy的,绑定层的内部会产生依赖所绑定中间件的代码来实现fidl中定义的接口函数。

2.3 Stub

​ 在核心层生成了HelloWorldStub.hpp,HelloWorldStubDefault.hpp这两个代码文件,其中包含HelloWorldStubAdapter,HelloWorldStubRemoteEvent, HelloWorldStub以及HelloWorldStubDefault这四个类,这四个类的关系如下:

​ 首先是HelloWorldStubAdapter类,其继承于CommonAPI::StubAdapter(实现了CommonAPI地址提供接口getAddress)和HelloWorld(实现了接口信息提供方法getInterfacegetInterfaceVersion)。

​ 所谓CommonAPI地址,包含三个成员(domain, interface, instance),这三个成员都是字符串:

namespace CommonAPI {
class Address {
public:
    COMMONAPI_EXPORT Address();
    ...

private:
    std::string domain_;     // 所在域
    std::string interface_;   // 接口名称
    std::string instance_;    // 实例名称

    friend COMMONAPI_EXPORT std::ostream &operator<<(std::ostream &_out, const Address &_address);
};
}

CommonAPI::StubAdapter中CommonAPI地址的来源是绑定层的StubAdapter(CommonAPI::SomeIP::StubAdapter)在初始化的时候赋值的

// Stub.hpp
namespace CommonAPI {
    class StubAdapter {
    public:
        virtual ~StubAdapter() {}
        inline const Address &getAddress() const { return address_; }

    protected:
        Address address_;  // 在绑定层的StubAdapter中赋值
    };
}

// StubAdapter.cpp
namespace CommonAPI {
namespace SomeIP {
class StubAdapter: virtual public CommonAPI::StubAdapter, public InterfaceHandler {   // 继承自核心层的StubAdapter
...
void
StubAdapter::init(std::shared_ptr<StubAdapter> instance) {
    (void) instance;
    // AddressTranslator保存了绑定层地址和核心层地址的映射关系
    // 这个映射关系在绑定层StubAdapter的initializer函数中插入的
    AddressTranslator::get()->translate(someipAddress_, address_);  // address_是CommonAPI::StubAdapter中的成员变量
}
...
}
}

​ 其次是HelloWorldStubRemoteEvent类,这个类目前基本是空的,因为这是个和属性相关的类,如果你的fidl中没有定义属性成员,那么生成的StubRemoteEvent类可能就是空的,对照官网上https://github.com/COVESA/capicxx-core-tools/tree/master/CommonAPI-Examples/E02Attributes这个带属性的范例生成的代码可以看到,有属性的fidl生成的StubRemoteEvent类是定义了一些属性相关的接口的,例如:

class E02AttributesStubRemoteEvent
{
public:
    virtual ~E02AttributesStubRemoteEvent() { }

    /// Verification callback for remote set requests on the attribute x
    virtual bool onRemoteSetXAttribute(const std::shared_ptr<CommonAPI::ClientId> _client, int32_t _value) = 0;
    /// Action callback for remote set requests on the attribute x
    virtual void onRemoteXAttributeChanged() = 0;
    /// Verification callback for remote set requests on the attribute a1
    virtual bool onRemoteSetA1Attribute(const std::shared_ptr<CommonAPI::ClientId> _client, ::v1::commonapi::examples::CommonTypes::a1Struct _value) = 0;
    /// Action callback for remote set requests on the attribute a1
    virtual void onRemoteA1AttributeChanged() = 0;
};

​ 也就是说,生成的这个StubRemoteEent类是个关于事件通知的接口类,是需要用户自己来实现内部接口来接收属性的变化通知。

​ 接者是HelloWorldStub类,这也是个接口类,继承自CommonAPI::Stub,CommonAPI::Stub最重要的工作是和CommonAPI::StubHelper打交道

namespace CommonAPI {
template<typename StubAdapter_, typename StubRemoteEventHandler_>
class Stub: public virtual StubBase {
public:
	// 初始化StubAdapter
	virtual StubRemoteEventHandler_* initStubAdapter(const std::shared_ptr<StubAdapter_> &_stubAdapter) = 0;
	// 返回StubAdapter
    inline const std::shared_ptr<StubAdapter_> getStubAdapter() const { return stubAdapter_.lock(); }

protected:
    std::weak_ptr<StubAdapter_> stubAdapter_;

​ 因此,HelloWorldStub类包含了初始化和返回StubAdapter的接口,此外还增加了fidl中sayHello接口的申明,也就是说HelloWorldStub接口类的实现类至少要实现initStubAdapter,getStubAdapter和sayHello三个接口。

​ 最后就是HelloWorldStubDefault类,它就是实现上面HelloWorldStub接口类中三个接口的实现类,代码如下:

class COMMONAPI_EXPORT_CLASS_EXPLICIT HelloWorldStubDefault
    : public virtual HelloWorldStub {
public:
    COMMONAPI_EXPORT HelloWorldStubDefault()
        : remoteEventHandler_(this),
          interfaceVersion_(HelloWorld::getInterfaceVersion()) {
    }

    COMMONAPI_EXPORT const CommonAPI::Version& getInterfaceVersion(std::shared_ptr<CommonAPI::ClientId> _client) {
        (void)_client;
        return interfaceVersion_;  // 返回接口版本
    }

    COMMONAPI_EXPORT HelloWorldStubRemoteEvent* initStubAdapter(const std::shared_ptr< HelloWorldStubAdapter> &_adapter) {
        CommonAPI::Stub<HelloWorldStubAdapter, HelloWorldStubRemoteEvent>::stubAdapter_ = _adapter;
        return &remoteEventHandler_;
    }

    COMMONAPI_EXPORT virtual void sayHello(const std::shared_ptr<CommonAPI::ClientId> _client, std::string _name, sayHelloReply_t _reply) {
        (void)_client;
        (void)_name;
        std::string message = "";
        _reply(message);
    }
    ...

​ HelloWorldStubDefault类是一个默认的生成的Stub接口实现类,里面的函数实现大多数是无用的,用户需要在HelloWorldStubDefault这个生成的默认Stub实现类上再做一次实现(继承并且实现),例如代码实例中的HelloWorldStubImpl类那样。

三. fdepl

​ fdepl文件生成绑定层的代码,会生成HelloWorldSomeIPProxyHelloWorldSomeIPStubAdapterInternalHelloWorldSomeIPStubAdapter这几个绑定层的类。

3.1 Proxy

HelloWorldSomeIPProxy类是绑定层生成的Proxy类,其构造时需要传入绑定层地址(CommonAPI::SomeIP::Address),CommonAPI::SomeIP::Address包含SomeIP服务实例的信息:

namespace CommonAPI {
namespace SomeIP {

class COMMONAPI_EXPORT Address {
	...
private:
    service_id_t service_;     // 服务ID
    instance_id_t instance_;   // 实例ID
    major_version_t major_version_;   // Max版本号
    minor_version_t minor_version_;   // Min版本号
}
}
}

​ 先来看下创建HelloWorldSomeIPProxy的过程,首先,用户需要在自己的应用程序中创建核心层的Proxy(HelloWorldProxy)

// HelloWorldClient.cpp
int main() {
	...
	// CommonAPI::Address
	// {
	// domainid = "local"
	// interface = HelloWorld::getInterface = "commonapi.HelloWorld:v1_0"
	// instance = "test"
	// }
	std::shared_ptr<HelloWorldProxy<>> myProxy = runtime->buildProxy<HelloWorldProxy>("local", "test");
    ...
}

​ 可以看到,创建核心层HelloWorldProxy的时候,对应的CommonAPI::Address地址已经提供出来了,然后runtime在创建的时候,会在调用核心层的工厂类(CommonAPI::SomeIP::Factory)的createProxy创建绑定层的HelloWorldSomeIPProxy对象,这个时候就需要从AddressTranslator中获取绑定层地址(SomeipIP的服务和实例信息)

// Factory.cpp

std::shared_ptr<CommonAPI::Proxy>
Factory::createProxy(
    const std::string &_domain,
    const std::string &_interface, const std::string &_instance,
    const ConnectionId_t &_connectionId) {

    auto proxyCreateFunctionsIterator
        = proxyCreateFunctions_.lower_bound(_interface);
    // 查找是否为该interface注册过SomeIP绑定层的Proxy的创建函数
    if (proxyCreateFunctionsIterator
            != proxyCreateFunctions_.end()) {   // 找到该CommonAPI interface注册的SomeIP绑定层Proxy创建函数
   		...

        CommonAPI::Address address(_domain, itsInterface, _instance);
        Address someipAddress;
        // 在AddressTranslator中查找CommonAPI地址对应的SomeIP地址
        if (AddressTranslator::get()->translate(address, someipAddress)) {
            std::shared_ptr<Connection> itsConnection
                = getConnection(_connectionId);
            if (itsConnection) {
            	// 使用注册的Proxy创建函数createHelloWorldSomeIPProxy创建HelloWorldSomeIPProxy
            	// 传入的CommonAPI::SomeIP::Address为{0x1234, 0x1, 1, 0}
                std::shared_ptr<Proxy> proxy
                    = proxyCreateFunctionsIterator->second(
                            someipAddress, itsConnection);
                if (proxy && proxy->init())
                    return proxy;
            }
        }
    }

    COMMONAPI_ERROR("Creating proxy for \"", _domain, ":", _interface, ":",
            _instance, "\" failed!");
    return nullptr;
}

​ AddressTranslator中CommonAPI地址和SomeIP绑定层地址的对应关系在生成的绑定层HelloWorldSomeIPProxy.cpp中插入的:

// HelloWorldSomeIPProxy.cpp
void initializeHelloWorldSomeIPProxy() {
    // 插入地址对应关系
    CommonAPI::SomeIP::AddressTranslator::get()->insert(
        "local:commonapi.HelloWorld:v1_0:test",   // CommonAPI Address
        0x1234, 0x1, 1, 0);     // CommonAPI::SomeIP Address
    // 注册绑定层SomeIP创建函数
    CommonAPI::SomeIP::Factory::get()->registerProxyCreateMethod(
        "commonapi.HelloWorld:v1_0",   // CommonAPI interface
        &createHelloWorldSomeIPProxy);   // SomeIP Proxy create function
}

std::shared_ptr<CommonAPI::SomeIP::Proxy> createHelloWorldSomeIPProxy(
    const CommonAPI::SomeIP::Address &_address,
    const std::shared_ptr<CommonAPI::SomeIP::ProxyConnection> &_connection) {
    return std::make_shared< HelloWorldSomeIPProxy>(_address, _connection);
}

​ 然后,生成的绑定层HelloWorldSomeIPProxy也是需要实现fidl中定义的sayHello接口的,其实现主要是依赖Deployment类来包装输入的参数,然后通过ProxyHelper类来完成参数的序列化和中间件通信接口的调用(ProxyHelper::callMethod):

void HelloWorldSomeIPProxy::sayHello(std::string _name, CommonAPI::CallStatus &_internalCallStatus, std::string &_message, const CommonAPI::CallInfo *_info) {
    CommonAPI::Deployable< std::string, CommonAPI::SomeIP::StringDeployment> deploy_name(_name, static_cast< CommonAPI::SomeIP::StringDeployment* >(nullptr));   // 输入参数
    CommonAPI::Deployable< std::string, CommonAPI::SomeIP::StringDeployment> deploy_message(static_cast< CommonAPI::SomeIP::StringDeployment* >(nullptr));   // 返回参数
    // 依赖ProxyHelper类发起someip的Method请求(REQUEST-RESPONSE)
    CommonAPI::SomeIP::ProxyHelper<
        ...
    >::callMethodWithReply(    // ProxyHelper类是个静态类,内部都是静态方法,不保存成员
        *this,    // 主要是提供HelloWorldSomeIPProxy内部的Connection指针
        CommonAPI::SomeIP::method_id_t(0x7b),
        false,
        false,
        (_info ? _info : &CommonAPI::SomeIP::defaultCallInfo),
        deploy_name,
        _internalCallStatus,
        deploy_message);
    _message = deploy_message.getValue();
}

​ 而ProxyHelper的callMethod函数则依赖Connetion类来完成中间件接口的调用。

template <typename Proxy_ = Proxy>
static void callMethod(...) {
	if (_proxy.isAvailable()) {  // 对端service为可用状态
		// 首先序列化参数
		OutputStream outputStream(_methodCall, _isLittleEndian);
        const bool success = SerializableArguments<InArgs_...>::serialize(outputStream, _inArgs...);
        ...
        // 通过Connection指针调用到vsomeip中间件接口
        bool success = _proxy.getConnection()->sendMessage(_methodCall);  
       	...
    } else {
    	...
    }
}

3.2 Stub

HelloWorldSomeIPStubAdapterInternalHelloWorldSomeIPStubAdapter的父类,此外,HelloWorldSomeIPStubAdapterInternal类也是绑定层对于核心层HelloWorldStubAdapter接口类的实现。

​ 总体来说,StubAdapter类是在Stub和中间件之间做适配功能的类,并且实现了核心层的StubAdapter接口类中定义的接口。

​ 先看下HelloWorldSomeIPStubAdapterInternal类,HelloWorldSomeIPStubAdapterInternal继承自核心层生成的HelloWorldStubAdapter接口和CommonAPI::SomeIP::StubAdapterHelper类。

​ HelloWorldSomeIPStubAdapterInternal类有获取内部属性的成员,其中包括Version信息的属性的getHelloWorldInterfaceVersionStubDispatcher成员,如果fidl中定义了业务的属性,则还会包含该业务属性的StubDispatcher成员,例如https://github.com/COVESA/capicxx-core-tools/tree/master/CommonAPI-Examples/E02Attributes 中fdepl生成的E02AttributesSomeIPStubAdapter.hpp文件中,E02AttributesSomeIPStubAdapterInternal类就有getXAttributeStubDispatcher,getA1AttributeStubDispatcher和getE02AttributesInterfaceVersionStubDispatcher,其中getXAttributeStubDispatcher对应fidl中定义的属性x,getA1AttributeStubDispatcher对应fidl中定义的属性a1。

​ SomeIPStubAdapterInternal类内部的StubDispatcher成员都是GetAttributeStubDispatcher类型的,实现了StubDispatcher接口,查看GetAttributeStubDispatcher类的实现可以看到内部成员函数dispatchMessage,这个函数就是通过Connection类调用SomeIP中间件将属性值通过消息的方式发送给客户端:

template <typename StubClass_, typename AttributeType_, typename AttributeDepl_ = EmptyDeployment>
class GetAttributeStubDispatcher: public StubDispatcher<StubClass_> {
public:
	...
	bool dispatchMessage(const Message &message, const std::shared_ptr<StubClass_> &stub,
      RemoteEventHandlerType* _remoteEventHandler,
      std::shared_ptr<ProxyConnection> _connection) {
		...
        return sendAttributeValueReply(message, stub, _connection);
    }
    
 protected:   
    inline bool sendAttributeValueReply(const Message &message, const std::shared_ptr<StubClass_>& stub,
      std::shared_ptr<ProxyConnection> _connection) {
      ...
      return _connection->sendMessage(reply);
 	}
 	...
 }

​ 当服务端进程收到客户端发送的对属性访问的SOMEIP method消息触发该接口的调用,调用栈如下:

Connection::handleStubReceive   // 从中间件收到client发送的消息
	// 处理消息(根据消息中的serviceID, instanceID找到对应的CommonAPI::SomeIP::StubAdapter
	// 例如E02AttributesSomeIPStubAdapterInternal,HelloWorldSomeIPStubAdapterInternal 
	StubManager::handleMessage 
		StubAdapterHelper::onInterfaceMessage  // StubAdapterHelper也是E02AttributesSomeIPStubAdapterInternal的父类
			StubAdapterHelper::findDispatcherAndHandle
				StubDispatcher::dispatchMessage  // 内部调用Stub对应的get属性值的方法获取当前属性值

​ 当服务端进程将属性的值通过SOMEIP消息回复给客户端的时候,首先是需要知道当前服务instance中该属性的值对不对,fidl文件生成的核心层Stub中提供这个属性的值的获取方法:

// src-gen/v1/commonapi/examples/E02AttributesStubDefault.hpp
COMMONAPI_EXPORT virtual const int32_t &getXAttribute() {
    return xAttributeValue_;
}

​ 也就是说如果有Stub对象,就可以调用其getXAttribute方法获取其x属性的当前值,然后在E02AttributesSomeIPStubAdapterInternal的构造函数中可以看到,HelloWorldSomeIPStubAdapterInternal构造函数中创建x属性对应的getXAttributeStubDispatcher对象时提供getXAttribute方法:

E02AttributesSomeIPStubAdapterInternal(
	...
	getXAttributeStubDispatcher(
            &::v1::commonapi::examples::E02AttributesStub::lockXAttribute,
            &::v1::commonapi::examples::E02AttributesStub::getXAttribute,  // 注册到内部getStubFunctor_成员中
            false,
            _stub->hasElement(0)),22
    ...
}

​ getXAttributeStubDispatcher是GetAttributeStubDispatcher类型的,其dispatchMessage方法中可以看到对get属性值方法的调用:

class GetAttributeStubDispatcher: public StubDispatcher<StubClass_> {
public:
	...
	bool dispatchMessage(const Message &message, const std::shared_ptr<StubClass_> &stub,
      	...
        return sendAttributeValueReply(message, stub, _connection);
    }
protected:
    inline bool sendAttributeValueReply(const Message &message, const std::shared_ptr<StubClass_>& stub,
    	  std::shared_ptr<ProxyConnection> _connection) {
    	Message reply = message.createResponseMessage();
        OutputStream outputStream(reply, isLittleEndian_);
    	...
    	// 获取属性值
    	auto deployable = CommonAPI::Deployable<AttributeType_, AttributeDepl_>((stub.get()->*getStubFunctor_)(clientId), depl_);
    	outputStream << deployable;  // 写入消息
        outputStream.flush();

        return _connection->sendMessage(reply);  // 发送属性值get-method消息的回复消息
   	}
}

HelloWorldSomeIPStubAdapter继承了HelloWorldSomeIPStubAdapterInternal,也就具备了通过绑定层调用SomeIP中间件进行通信的功能,在此基础上,主要增加了一个功能,就是在构造函数中增加了SomeIP地址和Connection对象的传入,有了这两个对象,HelloWorldSomeIPStubAdapterInternal的SomeIP通信功能才能真正工作起来。

public:
    HelloWorldSomeIPStubAdapter(const CommonAPI::SomeIP::Address &_address,
                                            const std::shared_ptr<CommonAPI::SomeIP::ProxyConnection> &_connection,
                                            const std::shared_ptr<CommonAPI::StubBase> &_stub)
        : CommonAPI::SomeIP::StubAdapter(_address, _connection),
          HelloWorldSomeIPStubAdapterInternal<_Stub, _Stubs...>(_address, _connection, _stub) {
    }

​ 那么这个构造函数什么时候调用的呢?是CommonAPI::SomeIP::Factory调用的,之前在核心层说过,生成的绑定层Stub代码会将HelloWorldSomeIPStubAdapter的创建函数和对应的CommonAPI::Address注册给Factory:

void initializeHelloWorldSomeIPStubAdapter() {
    CommonAPI::SomeIP::AddressTranslator::get()->insert(
        "local:commonapi.HelloWorld:v1_0:test",
         0x1234, 0x1, 1, 0);
    CommonAPI::SomeIP::Factory::get()->registerStubAdapterCreateMethod(   // 注册HelloWorldSomeIPStubAdapter的创建函数
        "commonapi.HelloWorld:v1_0",
        &createHelloWorldSomeIPStubAdapter);
}

​ 当我们在服务端代码中注册我们的HelloWorldStubImpl时,通过核心层调用到绑定层的SomeIP::Factory进行HelloWorldSomeIPStubAdapter对象的创建:

int main() {
    std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();
    std::shared_ptr<HelloWorldStubImpl> myService =
    	std::make_shared<HelloWorldStubImpl>();
    runtime->registerService("local", "test", myService);   // 这一步会调用到SomeIP::Factory创建HelloWorldSomeIPStubAdapter
    ...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/964686.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ELK模块封装starter

文章目录 1.combinations-elk-starter1.目录结构2.log4j2-spring.xml 从环境变量读取host和port3.ELKProperties.java 两个属性4.ELKAutoConfiguration.java 启用配置类5.ELKEnvironmentPreparedListener.java 监听器从application.yml中获取属性值6.spring.factories 注册监听…

KNN算法:从思想到实现(附代码)

引言 K最近邻算法&#xff08;K Nearest Neighbors, KNN&#xff09;是一种简单而有效的机器学习算法&#xff0c;用于分类和回归问题。其核心思想基于“近朱者赤&#xff0c;近墨者黑”&#xff0c;即通过测量不同特征值之间的距离来进行分类或预测数值。本文将详细介绍KNN的…

学前端框架之前,你需要先理解 MVC

MVC 软件架构设计模式鼎鼎大名&#xff0c;相信你已经听说过了&#xff0c;但你确定自己已经完全理解到 MVC 的精髓了吗&#xff1f; 如果你是新同学&#xff0c;没听过 MVC&#xff0c;那可以到网上搜一些文章来看看&#xff0c;不过你要有心理准备&#xff0c;那些文章大多都…

第十八章 视图

目录 一、概述 二、语法 2.1. 创建视图 2.2. 查询视图 2.3. 修改视图 2.4. 删除视图 2.5. 示例 三、检查选项 3.1. CASCADED&#xff08;级联&#xff09; 3.2. LOCAL&#xff08;本地&#xff09; 四、视图的更新 五、视图作用 5.1. 简单 5.2. 安全 5.3. 数据独…

【MySQL】第一弹---MySQL 在 Centos 7环境安装

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】 目录 1. 卸载不要的环境 2. 检查系统安装包 3. 卸载这些默认安装包 4. 获取mysql官方yum源 5. 安装mysql yum 源&am…

实验2 词法分析(一)

实验2 词法分析(一) [实验目的]&#xff1a; 1 . 熟悉给定的词法分析程序&#xff1b; 2 . 改进词法分析程序。 [实验内容]&#xff1a; 1.运行TESE编译演示.exe&#xff0c;观看词法分析程序的分析过程&#xff0c;理解词法分析的原理。并尝试在“TEST源程序输入框”输入一段…

【PyQt】PyQt工具栏

PyQt工具栏 在 PyQt 中创建工具栏主要涉及 QMainWindow、QToolBar 和 QAction 类 界面展示 基本示例 import sys from PyQt5.QtWidgets import QMainWindow, QApplication, QAction from PyQt5.QtGui import QIcon from PyQt5.QtCore import Qtclass MainWindow(QMainWindow…

STM32 串口收发数据包

接线图 HEX数据包接收 文本数据包接收 代码配置 发送HEX数据包 //存储发送或接收的载荷数据 uint8_t TX_Packet[4]; uint8_t RX_Packet[4];void Serial_SendPacket(void) {Serial_SendByte(0xFF);//发送包头Serial_SendArray(TX_Packet, 4);//发送4个载荷数据Serial_SendByte…

zabbix5.0.46版本源码安装

zabbix5.0.46版本源码安装 1.安装环境说明 本例中安装zabbix开源软件和zabbix运行所需的中间件和数据库apache、php和flyingdb&#xff0c;软件版本信息如下&#xff1a; 软件版本zabbix5.0.46apachehttpd-2.4.61aprapr-1.7.5apr-util1.6.3php7.3.24PostgreSQL16.6 主机操作…

[Android] IKTV专享版

[Android] IKTV专享版 链接&#xff1a;https://pan.xunlei.com/s/VOILXXuEd3ASo93c88UW79sxA1?pwd4tsw# 2025年2月最新免费K歌神器&#xff01;家庭KTV软件&#xff0c;手机平板电视盒子电脑都可用

【OS】AUTOSAR架构下的Interrupt详解(下篇)

目录 3.代码分析 3.1中断配置代码 3.2 OS如何找到中断处理函数 3.3 Os_InitialEnableInterruptSources实现 3.4 Os_EnableInterruptSource 3.5 DisableAllInterrupts 3.5.1Os_IntSuspendCat1 3.5.2 Os_InterruptDisableAllEnter 3.5.3 Disable二类中断 3.5.4 Disable一…

flutter 专题四十七 Flutter 应用启动流程分析

众所周知&#xff0c;任何应用程序的启动都是从main()函数开始的&#xff0c;Flutter也不例外&#xff0c;main.dart文件的main函数开始的&#xff0c;代码如下。 void main() > runApp(MyApp());main函数则调用的是runApp函数&#xff0c;源码如下。 void runApp(Widget …

html中的表格属性以及合并操作

表格用table定义&#xff0c;标签标题用caption标签定义&#xff1b;用tr定义表格的若干行&#xff1b;用td定义若干个单元格&#xff1b;&#xff08;当单元格是表头时&#xff0c;用th标签定义&#xff09;&#xff08;th标签会略粗于td标签&#xff09; table的整体外观取决…

大语言模型轻量化:知识蒸馏的范式迁移与工程实践

大语言模型轻量化&#xff1a;知识蒸馏的范式迁移与工程实践 &#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 摘要 在大型语言模型&#xff…

Go语言的转义字符

文章目录 1. Go语言的转义字符(escapechar)2. 小结和提示 1. Go语言的转义字符(escapechar) 说明:常用的转义字符有如下: \t : 表示一个制表符&#xff0c;通常使用它可以排版\n &#xff1a;换行符\\ &#xff1a;一个\\" &#xff1a;一个"\r &#xff1a;一个回…

Docker深度解析:容器与容器局域网

DockerFile 解析&#xff1a; DockerFile 描述出镜像文件需要的一些依赖配置和环境变量执行命令 docker build&#xff0c;将我们的 dockerfile 文件打包成一个镜像文件直接使用我们的容器运行到该镜像文件 CentOS 镜像&#xff1a; 运行镜像&#xff1a; docker run -it cent…

360手机刷机 360手机解Bootloader 360手机ROOT

360手机刷机 360手机解Bootloader 360手机ROOT 问&#xff1a;360手机已停产&#xff0c;现在和以后&#xff0c;能刷机吗&#xff1f; 答&#xff1a;360手机&#xff0c;是肯定能刷机的 360手机资源下载网站 360手机-360手机刷机RootTwrp 360os.top 360rom.github.io 一、…

C++输入输出(上)

cin和cout cin是C中提供的标准输入流对象,一般针对的是键盘,也就是从键盘上输入的字符流,使用 cin来进行数据的提取,cin一般是和 >> (流提取运算符) 配合使用的。 cin的功能和scanf是类似的 cout是C中提供的标准输出流对象,一般针对的是控制台的窗口,也就是将数据以字符…

【沐风老师】3DMAX混沌破碎插件ChaosFracture使用方法

3DMAX混沌破碎插件ChaosFracture&#xff0c;只需一键操作&#xff0c;即可轻松实现物体的破碎效果&#xff0c;同时确保外表面与内部断裂部分保持原有的材质ID和UVs信息&#xff0c;真实呈现细腻的破碎场景。 【适用版本】 3DMax9及更高版本&#xff08;建议使用3DMax2018以上…

e2studio开发RA2E1(8)----GPT定时器频率与占空比的设置

e2studio开发RA2E1.8--GPT定时器频率与占空比的设置 概述视频教学样品申请硬件准备参考程序源码下载选择计时器时钟源PWM(脉冲宽度调制)R_GPT_PeriodSet()函数说明R_GPT_DutyCycleSet()函数说明R_GPT_Reset()函数说明R_GPT_Close() 函数说明主程序波形情况 概述 GPT&#xff0…