一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ROS中的序列化實現(xiàn)

麥辣雞腿堡 ? 來源:古月居 ? 作者:古月居 ? 2023-09-14 17:26 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

理解了序列化,再回到ROS。我們發(fā)現(xiàn),ROS沒有采用第三方的序列化工具,而是選擇自己實現(xiàn),代碼在roscpp_core項目下的roscpp_serialization中,見下圖。這個功能涉及的代碼量不是很多。

為什么ROS不使用現(xiàn)成的序列化工具或者庫呢?可能ROS誕生的時候(2007年),有些序列化庫可能還不存在(protobuf誕生于2008年),更有可能是ROS的創(chuàng)造者認為當(dāng)時沒有合適的工具。

1.2.1 serialization.h

核心的函數(shù)都在serialization.h里,簡而言之,里面使用了C語言標準庫的memcpy函數(shù)把消息拷貝到流中。

下面來看一下具體的實現(xiàn)。

序列化功能的特點是要處理很多種數(shù)據(jù)類型,針對每種具體的類型都要實現(xiàn)相應(yīng)的序列化函數(shù)。

為了盡量減少代碼量,ROS使用了模板的概念,所以代碼里有一堆的template。

從后往前梳理,先看Stream這個結(jié)構(gòu)體吧。在C++里結(jié)構(gòu)體和類基本沒什么區(qū)別,結(jié)構(gòu)體里也可以定義函數(shù)。

Stream翻譯為流,流是一個計算機中的抽象概念,前面我們提到過字節(jié)流,它是什么意思呢?

在需要傳輸數(shù)據(jù)的時候,我們可以把數(shù)據(jù)想象成傳送帶上連續(xù)排列的一個個被傳送的物體,它們就是一個流。

更形象的,可以想象磁帶或者圖靈機里連續(xù)的紙帶。在文件讀寫、使用串口、網(wǎng)絡(luò)Socket通信等領(lǐng)域,流經(jīng)常被使用。例如我們常用的輸入輸出流:

cout<<"helllo"; 由于使用很多,流的概念也在演變。想了解更多可以看這里。

struct Stream
{
  // Returns a pointer to the current position of the stream
  inline uint8_t* getData() { return data_; }
  // Advances the stream, checking bounds, and returns a pointer to the position before it was advanced.
  // throws StreamOverrunException if len would take this stream past the end of its buffer
  ROS_FORCE_INLINE uint8_t* advance(uint32_t len)
{
    uint8_t* old_data = data_;
    data_ += len;
    if (data_ > end_)
    {
      // Throwing directly here causes a significant speed hit due to the extra code generated for the throw statement
      throwStreamOverrun();
    }
    return old_data;
  }
  // Returns the amount of space left in the stream
  inline uint32_t getLength() { return static_cast< uint32_t >(end_ - data_); }
  
protected:
  Stream(uint8_t* _data, uint32_t _count) : data_(_data), end_(_data + _count) {}


private:
  uint8_t* data_;
  uint8_t* end_;
};

注釋表明Stream是個基類,輸入輸出流IStream和OStream都繼承自它。

Stream的成員變量data_是個指針,指向序列化的字節(jié)流開始的位置,它的類型是uint8_t。

在Ubuntu系統(tǒng)中,uint8_t的定義是typedef unsigned char uint8_t;

所以uint8_t就是一個字節(jié),可以用size_of()函數(shù)檢驗。data_指向的空間就是保存字節(jié)流的。

輸出流類OStream用來序列化一個對象,它引用了serialize函數(shù),如下。

struct OStream : public Stream
{
  static const StreamType stream_type = stream_types::Output;
  OStream(uint8_t* data, uint32_t count) : Stream(data, count) {}
  /* Serialize an item to this output stream*/
  template< typename T >
  ROS_FORCE_INLINE void next(const T& t)
{
    serialize(*this, t);
  }
  template< typename T >
  ROS_FORCE_INLINE OStream& operator< const T& t)
  {
    serialize(*this, t);
    return *this;
  }
};

輸入流類IStream用來反序列化一個字節(jié)流,它引用了deserialize函數(shù),如下。

struct ROSCPP_SERIALIZATION_DECL IStream : public Stream
{
  static const StreamType stream_type = stream_types::Input;
  IStream(uint8_t* data, uint32_t count) : Stream(data, count) {}
  /* Deserialize an item from this input stream */
  template< typename T >
  ROS_FORCE_INLINE void next(T& t)
{
    deserialize(*this, t);
  }
  template< typename T >
  ROS_FORCE_INLINE IStream& operator >>(T& t)
  {
    deserialize(*this, t);
    return *this;
  }
};

自然,serialize函數(shù)和deserialize函數(shù)就是改變數(shù)據(jù)形式的地方,它們的定義在比較靠前的地方。它們都接收兩個模板,都是內(nèi)聯(lián)函數(shù),然后里面沒什么東西,只是又調(diào)用了Serializer類的成員函數(shù)write和read。所以,serialize和deserialize函數(shù)就是個二道販子。

// Serialize an object.  Stream here should normally be a ros::serialization::OStream
template< typename T, typename Stream >
inline void serialize(Stream& stream, const T& t)
{
  Serializer< T >::write(stream, t);
}
// Deserialize an object.  Stream here should normally be a ros::serialization::IStream
template< typename T, typename Stream >
inline void deserialize(Stream& stream, T& t)
{
  Serializer< T >::read(stream, t);
}

所以,我們來分析Serializer類,如下。我們發(fā)現(xiàn),write和read函數(shù)又調(diào)用了類型里的serialize函數(shù)和deserialize函數(shù)。

頭別暈,這里的serialize和deserialize函數(shù)跟上面的同名函數(shù)不是一回事。

注釋中說:“Specializing the Serializer class is the only thing you need to do to get the ROS serialization system to work with a type”(要想讓ROS的序列化功能適用于其它的某個類型,你唯一需要做的就是特化這個Serializer類)。

這就涉及到的另一個知識點——模板特化(template specialization)。

template< typename T > struct Serializer
{
  // Write an object to the stream.  Normally the stream passed in here will be a ros::serialization::OStream
  template< typename Stream >
  inline static void write(Stream& stream, typename boost::call_traits< T >::param_type t)
{
    t.serialize(stream.getData(), 0);
  }
   // Read an object from the stream.  Normally the stream passed in here will be a ros::serialization::IStream
  template< typename Stream >
  inline static void read(Stream& stream, typename boost::call_traits< T >::reference t)
{
    t.deserialize(stream.getData());
  }
  // Determine the serialized length of an object.
  inline static uint32_t serializedLength(typename boost::call_traits< T >::param_type t)
{
    return t.serializationLength();
  }
};

接著又定義了一個帶參數(shù)的宏函數(shù)ROS_CREATE_SIMPLE_SERIALIZER(Type),然后把這個宏作用到了ROS中的10種基本數(shù)據(jù)類型,分別是:uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double。

說明這10種數(shù)據(jù)類型的處理方式都是類似的??吹竭@里大家應(yīng)該明白了,write和read函數(shù)都使用了memcpy函數(shù)進行數(shù)據(jù)的移動。

注意宏定義中的template<>語句,這正是模板特化的標志,關(guān)鍵詞template后面跟一對尖括號。

關(guān)于模板特化可以看這里。

#define ROS_CREATE_SIMPLE_SERIALIZER(Type) 
  template<  > struct Serializer Type > 
  { 
    template< typename Stream > inline static void write(Stream& stream, const Type v) 
{ 
      memcpy(stream.advance(sizeof(v)), &v, sizeof(v) ); 
    } 
    template< typename Stream > inline static void read(Stream& stream, Type& v) 
{ 
      memcpy(&v, stream.advance(sizeof(v)), sizeof(v) ); 
    } 
    inline static uint32_t serializedLength(const Type&) 
{ 
      return sizeof(Type); 
    } 
};
ROS_CREATE_SIMPLE_SERIALIZER(uint8_t)
ROS_CREATE_SIMPLE_SERIALIZER(int8_t)
ROS_CREATE_SIMPLE_SERIALIZER(uint16_t)
ROS_CREATE_SIMPLE_SERIALIZER(int16_t)
ROS_CREATE_SIMPLE_SERIALIZER(uint32_t)
ROS_CREATE_SIMPLE_SERIALIZER(int32_t)
ROS_CREATE_SIMPLE_SERIALIZER(uint64_t)
ROS_CREATE_SIMPLE_SERIALIZER(int64_t)
ROS_CREATE_SIMPLE_SERIALIZER(float)
ROS_CREATE_SIMPLE_SERIALIZER(double)

對于其它類型的數(shù)據(jù),例如bool、std::string、std::vector、ros::Time、ros::Duration、boost::array等等,它們各自的處理方式有細微的不同,所以不再用上面的宏函數(shù),而是用模板特化的方式每種單獨定義,這也是為什么serialization.h這個文件這么冗長。

對于int、double這種單個元素的數(shù)據(jù),直接用上面特化的Serializer類中的memcpy函數(shù)實現(xiàn)序列化。

對于vector、array這種多個元素的數(shù)據(jù)類型怎么辦呢?方法是分成幾種情況,對于固定長度簡單類型的(fixed-size simple types),還是用各自特化的Serializer類中的memcpy函數(shù)實現(xiàn),沒啥太大區(qū)別。

對于固定但是類型不簡單的(fixed-size non-simple types)或者既不固定也不簡單的(non-fixed-size, non-simple types)或者固定但是不簡單的(fixed-size, non-simple types),用for循環(huán)遍歷,一個元素一個元素的單獨處理。

那怎么判斷一個數(shù)據(jù)是不是固定是不是簡單呢?這是在roscpp_traits文件夾中的message_traits.h完成的。

其中采用了萃取Type Traits,這是相對高級一點的編程技巧了,筆者也不太懂。

對序列化的介紹暫時就到這里了,有一些細節(jié)還沒講,等筆者看懂了再補。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 機器人
    +關(guān)注

    關(guān)注

    213

    文章

    29756

    瀏覽量

    213044
  • 操作系統(tǒng)
    +關(guān)注

    關(guān)注

    37

    文章

    7154

    瀏覽量

    125647
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4381

    瀏覽量

    64945
  • ROS
    ROS
    +關(guān)注

    關(guān)注

    1

    文章

    288

    瀏覽量

    17761
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    如何使用Serde進行序列化和反序列化

    。它是 Rust 生態(tài)中最受歡迎的序列化庫之一。 基礎(chǔ)用法 安裝 在 Rust 項目中使用 Serde,需要在 Cargo.toml 文件添加如下依賴: [dependencies] serde
    的頭像 發(fā)表于 09-30 17:09 ?1699次閱讀

    Java序列化的機制和原理

    ,我們用一個實例來示范序列化以后的字節(jié)是如何描述一個對象的信息的。序列化的必要性Java,一切都是對象,在分布式環(huán)境中經(jīng)常需要將Object從這一端網(wǎng)絡(luò)或設(shè)備傳遞到另一端。這就需要有一種可以在兩端傳輸
    發(fā)表于 07-10 07:27

    c語言序列化和反序列化有何區(qū)別

    這里寫自定義目錄標題c語言序列化和反序列化tplut.htplut.c測試代碼參考c語言序列化和反序列化網(wǎng)絡(luò)調(diào)用,數(shù)據(jù)傳輸都需要把數(shù)據(jù)序列化
    發(fā)表于 07-14 07:32

    SpringMVC JSON框架的自定義序列化與反序列化

    限于createTime和updateTime,更貼近于需求缺點就是需要轉(zhuǎn)換的字段都需要使用注解,工作量有點大當(dāng)然有其他的統(tǒng)一處理方案,這里不贅述。自定義反序列化在jackson框架上實現(xiàn)自定義序列化
    發(fā)表于 10-10 16:02

    理解PHP反序列化漏洞

    理解PHP反序列化漏洞
    發(fā)表于 09-07 11:03 ?7次下載
    理解PHP反<b class='flag-5'>序列化</b>漏洞

    java序列化和反序列化范例和JDK類庫序列化API

    存放在一個文件; 2) 在網(wǎng)絡(luò)上傳送對象的字節(jié)序列。 在很多應(yīng)用,需要對某些對象進行序列化,讓它們離開內(nèi)存空間,入住物理硬盤,以便長期保存。比如最常見的是Web服務(wù)器
    發(fā)表于 09-27 10:13 ?6次下載

    static屬性為什么不會被序列化

    實現(xiàn)序列化和反序列化為什么要實現(xiàn)Serializable接口?
    的頭像 發(fā)表于 07-15 11:03 ?2048次閱讀

    C#實現(xiàn)對象序列化的三種方式是什么

    很多小伙伴一提到序列化,都會想到二進制序列化,但其實序列化并不僅僅只是二進制序列化,我們常說的對象序列化有三種方式,分別是二進制
    的頭像 發(fā)表于 02-22 16:11 ?1495次閱讀
    C#<b class='flag-5'>實現(xiàn)</b>對象<b class='flag-5'>序列化</b>的三種方式是什么

    python序列化對象

    序列化對象:將對象轉(zhuǎn)換為可以存儲或傳輸?shù)男问健? (1) 用于存儲:將對象的字節(jié)序列存儲到文件,程序退出后不會消失,便于后續(xù)使用。
    的頭像 發(fā)表于 03-10 09:57 ?2537次閱讀

    ROS機器人操作系統(tǒng)的實現(xiàn)原理(上)

    本文介紹ROS機器人操作系統(tǒng)(Robot Operating System)的實現(xiàn)原理,從最底層分析ROS代碼是如何實現(xiàn)的。 **1、序列化
    的頭像 發(fā)表于 05-19 17:41 ?1239次閱讀
    <b class='flag-5'>ROS</b>機器人操作系統(tǒng)的<b class='flag-5'>實現(xiàn)</b>原理(上)

    ROS機器人操作系統(tǒng)的實現(xiàn)原理(下)

    本文介紹ROS機器人操作系統(tǒng)(Robot Operating System)的實現(xiàn)原理,從最底層分析ROS代碼是如何實現(xiàn)的。 **1、序列化
    的頭像 發(fā)表于 05-19 17:42 ?1450次閱讀

    什么是序列化 為什么要序列化

    什么是序列化? “序列化”(Serialization )的意思是將一個對象轉(zhuǎn)化為字節(jié)流。 這里說的對象可以理解為“面向?qū)ο蟆崩锏哪莻€對象,具體的就是存儲在內(nèi)存的對象數(shù)據(jù)。 與之相反的過程是“反
    的頭像 發(fā)表于 09-14 17:22 ?3319次閱讀
    什么是<b class='flag-5'>序列化</b> 為什么要<b class='flag-5'>序列化</b>

    如何用C語言進行json的序列化和反序列化

    json是目前最為流行的文本數(shù)據(jù)傳輸格式,特別是在網(wǎng)絡(luò)通信上廣泛應(yīng)用,隨著物聯(lián)網(wǎng)的興起,在嵌入式設(shè)備上,也需要開始使用json進行數(shù)據(jù)傳輸,那么,如何快速簡潔地用C語言進行json的序列化和反序列化
    的頭像 發(fā)表于 10-07 11:05 ?2027次閱讀

    Java序列化怎么使用

    轉(zhuǎn)換方式就叫做序列化。將文件或者網(wǎng)絡(luò)傳輸得到的 byte[] 數(shù)組轉(zhuǎn)換為 java 對象就叫做反序列化。 怎么使用 如果一個 Java 對象要能被序列化,必須
    的頭像 發(fā)表于 10-10 14:19 ?663次閱讀

    什么時候需要Boost序列化

    程序開發(fā),序列化是經(jīng)常需要用到的。像一些相對高級語言,比如JAVA, C#都已經(jīng)很好的支持了序列化,那么C++呢?當(dāng)然一個比較好的選擇就是用Boost,這個號稱C++準標準庫的東西。 什么時候需要
    的頭像 發(fā)表于 11-10 10:14 ?675次閱讀