☞返回总目录
5.4.6 方法
骨架侧的服务方法是抽象方法,必须由继承骨架的服务实现子类进行重写。让我们来看一下我们服务示例中的 Adjust 方法:
/*** 对于所有输出和非空返回参数* 生成一个包含非空返回值和/或输出参数的封装结构。*/
struct AdjustOutput
{bool success;Position effective_position;
};virtual ara::core::Future<AdjustOutput> Adjust(const Position& position) = 0;
服务方法抽象定义中的IN-parameters直接映射到服务方法签名的方法参数。在这个用例中,IN-parameter是来自Position类型的position参数,由于它是非基本类型,因此被建模为 “const ref”(常量引用)。
服务方法签名的有趣部分是返回类型,服务方法的实现必须返回我们广泛讨论的ara::core::Future。这个想法很简单:我们不想强迫服务方法实现者通过这个 “入口点” 方法的简单返回信号来表示服务方法的完成!
也许服务方法实现者决定将服务调用的实际处理分派到一个中央工作线程池!那么,当 “入口点” 方法的返回表示服务调用完成并通知给通信管理时,这将非常糟糕。然后,在我们的工作线程池场景中,我们将不得不阻塞在服务方法内部的某种等待点,并等待工作线程的通知,表明它已经完成,只有这样我们才会从服务方法返回。在这种情况下,我们的服务方法内部将有一个阻塞的线程!从现代多核 CPU 的有效使用角度来看,这是不可接受的。
此部分内容难懂,可以结合以下图形理解此部分内容
返回的ara::core::Future包含一个作为模板参数的结构,该结构聚合了服务调用的所有输出参数。
以下两个代码示例展示了 Adjust 实现的两种变体,在第一种变体中,服务方法在方法体中直接同步处理,返回一个已经设置结果的 ara::core::Future;而在第二种示例中,工作被分派到异步工作线程,返回的ara::core::Future在返回时可能没有设置结果。
using namespace ara::com;/*** 对RadarService的实现*/
class RadarServiceImpl : public RadarServiceSkeleton
{
public:Future<AdjustOutput> Adjust(const Position& position){ara::core::Promise<AdjustOutput> promise;// 调用同步等待内整函数处理完毕,该函数提供结果struct AdjustOutput out = doAdjustInternal(position, &out.effective_position);promise.set_value(out);// 返回一个已经设置承诺的未来结果return promise.get_future();}private:AdjustOutput doAdjustInternal(const Position& position) {//... 实现}
};
正如您在上面的示例中看到的:在服务方法的主体内部,调用了一个内部方法doAdjustInternal(),该方法同步执行工作。即,在 doAdjustInternal()返回后,在 out 中设置了类似于服务方法输出参数的属性。然后,将这个 out 值设置在 ara::core::Promise 上,然后从 Promise 返回给创建的Future。
这具有这样的效果:获得此 Future 作为返回的调用者可以立即调用 Future::get (),它不会阻塞,而是立即返回 AdjustOutput。
现在让我们看一下异步工作线程变体:
using namespace ara::com;/*** 我们对RadarService的实现*/
class RadarServiceImpl : public RadarServiceSkeleton
{
public:Future<AdjustOutput> Adjust(const Position& position){ara::core::Promise<AdjustOutput> promise;auto future = promise.get_future();// 在新线程中异步调用内部调整函数std::thread t([this] (const Position& pos, ara::core::Promise prom) {prom.set_value(doAdjustInternal(pos));},std::cref(position), std::move(promise)).detach();// 我们返回一个此时可能已设置或未设置的未来...return future;}private:AdjustOutput doAdjustInternal(const Position& position) {//... 实现}
};
在这个示例中,doAdjustInternal() 在一个不同的异步线程中被调用:在一个小的 lambda 函数中调用了doAdjustInternal() ,并且,该lambda 函数负责将doAdjustInternal()处理的结果设置到Promise对象。
5.4.6.1 单向(One - Way)又名即发即忘(Fire - and - Forget)方法
骨架侧的 “单向 / 即发即忘” 方法与普通方法相比,签名更简单。由于不需要向调用者提供反馈,所以它是一个简单的 void 方法:
virtual void LogCurrentState() = 0;
5.4.6.1 引发应用程序错误
每当在服务方法实现检测到应用程序错误(根据接口描述判定)时,必须把该应用程序错误的错误码简单地存储到 Promise 中,Future 将从该 Promise 返回给调用者:
using namespace ara::com;
using namespace com::mycompany::division::radarservice;/*** 我们对 RadarService 的实现*/
class RadarServiceImpl : public RadarServiceSkeleton
{
public:Future<CalibrateOutput> Calibrate(const std::string& configuration){ara::core::Promise<CalibrateOutput> promise;auto future = promise.get_future();// 我们检查给定的配置参数if (!checkConfigString(configuration)){ // 给定的参数无效:// 假设在 ARXML 中我们有一个名为 SpecificErrors 的错误域// 其中包含 InvalidConfigString 错误。// 请注意,数字错误码将隐式转换为 ara::core::ErrorCodepromise.SetError(SpecificErrorsErrc::InvalidConfigString);}else{//...}// 我们返回一个可能设置了异常的未来return future;}private:bool checkConfigString(const std::string& config);std::string curValidConfig_;
};
在这个示例中,Calibrate() 的实现检测到给定的配置字符串参数无效,并将相应的异常设置到 Promise 中。