親自上陣!C++大佬深度“劇透”:C++26將如何在代碼生成上對(duì)抗Rust?

點(diǎn)擊上方“C語(yǔ)言與CPP編程”,選擇“關(guān)注/置頂/星標(biāo)公眾號(hào)

干貨福利,第一時(shí)間送達(dá)!

最近有小伙伴說(shuō)沒有收到當(dāng)天的文章推送,這是因?yàn)槲⑿鸥牧送扑蜋C(jī)制,導(dǎo)致沒有星標(biāo)公眾號(hào)的小伙伴刷不到當(dāng)天推送的文章,無(wú)法接收到一些比較實(shí)用的知識(shí)和資訊。所以建議大家加個(gè)星標(biāo)??,以后就能第一時(shí)間收到推送了。

【編者按】隨著編程語(yǔ)言的不斷發(fā)展,Rust 和即將推出的 C++26 在代碼生成領(lǐng)域的對(duì)比越來(lái)越受到開發(fā)者和研究者的關(guān)注。本文作者身為?C++ 標(biāo)準(zhǔn)委員會(huì)成員,將重點(diǎn)討論 Rust 的過(guò)程宏并分析其工作原理,并基于此展示其是如何為 C++26 提出截然不同的解決方案的。

原文鏈接:https://brevzin.github.io/c++/2024/09/30/annotations/


我很喜歡做的一件事,就是比較不同編程語(yǔ)言如何解決相同的問題,尤其是當(dāng)這些語(yǔ)言采取了截然不同的方法時(shí),我覺得這非常具有教育意義。在這篇文章中,我們將嘗試把反射(reflection)這一顛覆性的語(yǔ)言特性引入到 C++26 標(biāo)準(zhǔn)中。從根本上來(lái)講,反射可以分為兩大部分:

1、自?。↖ntrospection):在編譯期間,能夠?qū)Τ绦蜻M(jìn)行查詢的能力。

2、代碼生成(Code Generation):讓程序自動(dòng)生成新代碼的能力。

針對(duì) C++26 的 P2996 提案是一個(gè)處理自省問題的核心提案,它為未來(lái)擴(kuò)展反射功能奠定了基礎(chǔ),涵蓋多個(gè)方向的延展功能(例如 P3294 的代碼生成設(shè)計(jì))。然而,雖然自省功能本身非常有用,但它只解決了一半的問題——知名 C++ 技術(shù)專家 Andrei Alexandrescu 甚至在 CppCon 大會(huì)上宣稱,如果沒有代碼生成,自省幾乎是“無(wú)用的”。

目前,C++ 確實(shí)有一種代碼生成功能:C 宏(C Macros)。不過(guò),這種機(jī)制非常原始,且存在許多局限。首先,C 宏缺乏嚴(yán)格的語(yǔ)法規(guī)則,甚至可能在不知情的情況下調(diào)用宏(標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)對(duì)此有保護(hù)措施)。其次,實(shí)現(xiàn)一些簡(jiǎn)單的邏輯(如迭代或條件判斷)往往需要相當(dāng)復(fù)雜的技巧。然而,盡管存在這些問題,在某些場(chǎng)景下,C 宏仍然是最好的解決方案——這也反映了我們迫切需要更完善的代碼生成機(jī)制。

另一方面,Rust 雖然沒有任何自省功能,但它擁有成熟的代碼生成機(jī)制,特別是其聲明式和過(guò)程宏。因此本文將重點(diǎn)討論 Rust 的過(guò)程宏,尤其是派生宏(derive macro)。我們將通過(guò)兩個(gè)示例展示派生宏如何解決問題,分析其工作原理,以及我們?nèi)绾螢?C++26 提出截然不同的解決方案。

不過(guò),我不是專業(yè) Rust 程序員,因此如果我在文中犯了錯(cuò)誤,還請(qǐng)大家指正。更新一下,在發(fā)布這篇博客后,有人指出我在一些地方犯了錯(cuò)誤,我已經(jīng)進(jìn)行了更正。這些錯(cuò)誤包括:我曾提到 Rust 屬性無(wú)法接受任意值(其實(shí)它是可以的,只是舊版本選擇不這么做),以及有比我提到的更好的方式來(lái)解析屬性(實(shí)際上大多數(shù)人都采用類似做法)。


1、結(jié)構(gòu)體的美化打印(Pretty-Printing)

當(dāng)你學(xué)會(huì)如何聲明一個(gè)帶有新成員的類型后,很可能會(huì)想讓這個(gè)類型進(jìn)行調(diào)試打印(debug-printable)。不僅是因?yàn)檎{(diào)試打印在日常開發(fā)中非常有用,還因?yàn)樵?Rust 中實(shí)現(xiàn)這一功能非常簡(jiǎn)單:

#[derive(Debug)]struct Point {    x: i32,    y: i32,}
fn main() { let p = Point { x: 1, y: 2 }; // prints: p=Point { x: 1, y: 2 } println!("p={p:?}");}

代碼的第一行通過(guò) #[derive(Debug)] 讓 Point 結(jié)構(gòu)體支持調(diào)試打印。它的作用是自動(dòng)生成代碼,使得可以打印類型名稱以及所有成員的名稱和值,并按順序輸出。

在我手頭的《Rust 編程語(yǔ)言》書中,第 82 頁(yè)就展示了如何聲明一個(gè) struct,第 89 頁(yè)則展示了如何讓它支持調(diào)試打印,這幾乎是 Rust 學(xué)習(xí)過(guò)程中最早會(huì)遇到的功能之一。對(duì)于這個(gè)任務(wù),Rust 還提供了另一個(gè)簡(jiǎn)便的方式:dbg!(p),不過(guò)這里我使用 println! 是為了更貼近未來(lái)在 C++ 中實(shí)現(xiàn)類似功能的方式。

由于這是編譯時(shí)的注解(annotation),如果以后我為 Point 結(jié)構(gòu)體添加了一個(gè)新字段(比如我決定將其擴(kuò)展為三維結(jié)構(gòu)體,添加一個(gè) z 字段),調(diào)試打印的輸出也會(huì)自動(dòng)更新,以打印新字段的值。

總結(jié)來(lái)說(shuō)就是:非常簡(jiǎn)單!

你可能會(huì)問,這究竟是如何實(shí)現(xiàn)的?是什么使得宏和 Debug 特性(trait)能夠?qū)崿F(xiàn)這種交互?正如我之前提到的,不同于我們?yōu)?C++26 提出的方案,Rust 沒有任何形式的自?。╥ntrospection)功能,也沒有機(jī)制可以查詢結(jié)構(gòu)體的成員并對(duì)其進(jìn)行迭代。

相反,Rust 的 derive 宏采用了非常不同的方式:它是一個(gè)函數(shù),接收被注解結(jié)構(gòu)體的 Token 流作為輸入,生成相應(yīng)的 Token 流代碼并注入到代碼中。實(shí)際上,這些注入的代碼并不一定與輸入直接相關(guān)。

在這種情況下,我們通過(guò)獲取 Point 結(jié)構(gòu)體的?Token?流輸入,解析它,并使用解析結(jié)果生成我們需要的輸出,從而繞過(guò)了缺乏自省的問題。我想這也算是一種“自省”——只不過(guò)它只能在特定情況下明確選擇使用。

在上面的例子中,derive 宏生成了如下代碼(我使用 cargo expand 得到的結(jié)果):

#[automatically_derived]impl ::core::fmt::Debug for Point {    #[inline]    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {        ::core::fmt::Formatter::debug_struct_field2_finish(            f,            "Point",            "x",            &self.x,            "y",            &&self.y,        )    }}

這個(gè)代碼看起來(lái)并不復(fù)雜,但關(guān)鍵在于 Rust 程序員無(wú)需手動(dòng)編寫這些模板代碼。他們只需要學(xué)習(xí)如何寫一行代碼(實(shí)際上,連一整行都不需要):#[derive(Debug)]。這就是代碼生成的強(qiáng)大之處。

即便如此,這個(gè)結(jié)果也很有趣。為什么對(duì) self.x 使用 &self.x,而對(duì) self.y 使用 &&self.y 呢?這與 Rust 無(wú)法進(jìn)行自省功能有關(guān)。在 Rust 中,最后一個(gè)字段可以是不定長(zhǎng)類型(unsized type)。不定長(zhǎng)類型可以被打印,但是需要一個(gè)額外的間接層。derive 宏無(wú)法知道 y 是否是定長(zhǎng)的(在這個(gè)例子中它是 i32,所以是定長(zhǎng)的),所以為了支持兩種情況,宏預(yù)先添加了這個(gè)額外的間接層。

在 C++ 中,如果想要提出的方案盡量貼近 Rust 的語(yǔ)法,可以這樣實(shí)現(xiàn):

struct [[=derive]] Point {    int x;    int y;};
int main() { auto p = Point{.x=1, .y=2}; // prints p=Point{.x=1, .y=2} std::println("p={}", p);}

從本質(zhì)上講,C++ 和 Rust 的格式化機(jī)制有一些相似之處在 Rust 中,你必須為 Debug trait 提供一個(gè) impl。而在 C++ 中,你需要特化 std::formatter(我們不區(qū)分 Debug 和 Display)。正如我之前展示的,Rust 的宏調(diào)用會(huì)為類型注入正確的 impl Debug 代碼,而在 C++ 中,我們并沒有這樣做。

我在這里使用的特性叫做“注解”(annotation),這個(gè)功能將在 P3394 提案中提出,首次由 Daveed Vandevoorde 在 CppCon 的閉幕演講中披露。這個(gè)提案的目標(biāo)是讓你能夠以一種自省可以觀察到的方式標(biāo)注聲明。值得注意的是,這里并沒有發(fā)生任何代碼注入,我們只是稍微擴(kuò)展了一下自省功能。

然而,鑒于 C++?本身具備自省功能(或者將隨著 P2996 的提出而獲得),這已經(jīng)足夠完成我們的目標(biāo)。我們可以提前提供一個(gè)特化的 std::formatter,該特化會(huì)在類型帶有 derive 注解時(shí)啟用,而這個(gè)注解本質(zhì)上只是一個(gè)空值:

template <auto V> struct Derive { };template <auto V> inline constexpr Derive derive;
inline constexpr struct{} Debug;
template <class T> requires (has_annotation(^^T, derive))struct std::formatter { // ...};

一旦我們有了這個(gè)基礎(chǔ),特化的實(shí)現(xiàn)就可以對(duì)類型?T?進(jìn)行自省,獲取我們需要的所有信息,以便展示:我們可以迭代所有非靜態(tài)的數(shù)據(jù)成員,格式化它們的名稱和值。一個(gè)簡(jiǎn)化的實(shí)現(xiàn)如下:

template <class T> requires (has_annotation(^^T, derive))struct std::formatter {    constexpr auto parse(auto& ctx) { return ctx.begin(); }
auto format(T const& m, auto& ctx) const { auto out = std::format_to(ctx.out(), "{}", display_string_of(^^T)); *out++ = '{';
bool first = true; [:expand(nonstatic_data_members_of(^^T)):] >> [&]<auto nsdm>{ if (not first) { *out++ = ','; *out++ = ' '; } first = false;
out = std::format_to(out, ".{}={}", identifier_of(nsdm), m.[:nsdm:]); };
*out++ = '}'; return out; }};

某種意義上來(lái)說(shuō),我們?nèi)匀皇窃谏纱a——模板實(shí)際上就是 C++ 中的一種代碼生成形式。但有趣的是,在這里我們通過(guò)非常不同的機(jī)制實(shí)現(xiàn)了相同的目標(biāo)。

請(qǐng)注意,這就是完整的實(shí)現(xiàn)代碼,可以看到代碼量其實(shí)并不多。


2、JSON 序列化

在之前討論的調(diào)試打印示例中,我們只是簡(jiǎn)單地按順序打印所有成員。那么如果我們想做些更復(fù)雜的操作呢?在處理序列化時(shí),有時(shí)字段的名稱可能需要與原始的成員名不同。還有些情況,目標(biāo)格式在編程語(yǔ)言中根本無(wú)法直接表達(dá)——比如字段名可能是語(yǔ)言中的關(guān)鍵字,或者字段名包含空格等等。

因此,Rust 的 serde 庫(kù)提供了許多注解屬性,可以添加到類型和成員上,以控制序列化邏輯。下面是一個(gè)簡(jiǎn)單的例子:

use serde::Serialize;use serde_json;
#[derive(Serialize)]struct Person { #[serde(rename = "first name")] first: String,
#[serde(rename = "last name")] last: String,}
fn main() { let person = Person { first: "Peter".to_owned(), last: "Dimov".to_owned(), }; let j = serde_json::to_string(&person).unwrap();
// prints {"first name":"Peter","last name":"Dimov"} println!("{}", j);}

類似于?Debug?特性,Serialize?的派生宏會(huì)為我們注入一個(gè)實(shí)現(xiàn),其生成的代碼如下:

#[doc(hidden)]#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]const _: () = {    #[allow(unused_extern_crates, clippy::useless_attribute)]    extern crate serde as _serde;    #[automatically_derived]    impl _serde::Serialize for Person {        fn serialize<__S>(            &self,            __serializer: __S,        ) -> _serde::__private::Result<__S::Ok, __S::Error>        where            __S: _serde::Serializer,        {            let mut __serde_state = _serde::Serializer::serialize_struct(                __serializer,                "Person",                false as usize + 1 + 1,            )?;            _serde::ser::SerializeStruct::serialize_field(                &mut __serde_state,                "first name",                &self.first,            )?;            _serde::ser::SerializeStruct::serialize_field(                &mut __serde_state,                "last name",                &self.last,            )?;            _serde::ser::SerializeStruct::end(__serde_state)        }    }};

在這里,你可以看到想要序列化的字段名(如?"first name"?和?"last name")與實(shí)際的成員綁定在一起。需要注意的是,false as usize + 1 + 1?是用來(lái)表示要序列化的字段數(shù)量的構(gòu)造,這里的 2 顯然是字段的數(shù)量。

如果我們要添加一個(gè)中間名,并且只有當(dāng)它非空時(shí)才進(jìn)行序列化,可以使用 skip_serializing_if 屬性:

#[derive(Serialize)]struct Person {    #[serde(rename = "first name")]    first: String,
#[serde(rename = "middle name", skip_serializing_if = "String::is_empty")] middle: String,
#[serde(rename = "last name")] last: String,}

生成的代碼如下,具體新增部分為第 18-19 行、第 26-37 行:

#[doc(hidden)]#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]const _: () = {    #[allow(unused_extern_crates, clippy::useless_attribute)]    extern crate serde as _serde;    #[automatically_derived]    impl _serde::Serialize for Person {        fn serialize<__S>(            &self,            __serializer: __S,        ) -> _serde::__private::Result<__S::Ok, __S::Error>        where            __S: _serde::Serializer,        {            let mut __serde_state = _serde::Serializer::serialize_struct(                __serializer,                "Person",                false as usize + 1 + if String::is_empty(&self.middle) { 0 } else { 1 }                    + 1,            )?;            _serde::ser::SerializeStruct::serialize_field(                &mut __serde_state,                "first name",                &self.first,            )?;            if !String::is_empty(&self.middle) {                _serde::ser::SerializeStruct::serialize_field(                    &mut __serde_state,                    "middle name",                    &self.middle,                )?;            } else {                _serde::ser::SerializeStruct::skip_field(                    &mut __serde_state,                    "middle name",                )?;            }            _serde::ser::SerializeStruct::serialize_field(                &mut __serde_state,                "last name",                &self.last,            )?;            _serde::ser::SerializeStruct::end(__serde_state)        }    }};

但在 C++?中,我們并沒有像?serde?這樣的庫(kù),它可以分離序列化的字段名和成員變量的名稱,至少我目前不知道有這樣的庫(kù)。C++ 中,通常是 JSON 庫(kù)處理 JSON 序列化,TOML 庫(kù)處理 TOML 序列化等。也許這是因?yàn)?C++ 缺乏像 Rust 那樣的語(yǔ)言支持,所以無(wú)法輕松實(shí)現(xiàn)這種序列化機(jī)制?

老實(shí)說(shuō),雖然在格式化方面 Rust 和 C++?的實(shí)現(xiàn)有些相似,但在序列化的靈活性上 Rust 確實(shí)更具優(yōu)勢(shì)。盡管 C++ 中沒有完全類似 serde 的庫(kù),但我們也可以使用類似 Boost.JSON 這樣的庫(kù)來(lái)實(shí)現(xiàn)序列化。

我們從支持 derive 和 rename 開始,這是為了讓代碼能夠正常工作的全部需求:

struct [[=derive]] Point {    int x, y;};
struct [[=derive]] Person { [[=serde::rename("first name")]] std::string first; [[=serde::rename("last name")]] std::string last;};
int main() { // prints {"x":1,"y":2} std::cout << boost::json::value_from(Point{.x=1, .y=2}) << '\n'; // prints {"first name":"Peter","last name":"Dimov"} std::cout << boost::json::value_from(Person{.first="Peter", .last="Dimov"}) << '\n';}

整段代碼只有 21 行,如果我保持與之前相同的模板形式,那么基本通過(guò)?derive?可以實(shí)現(xiàn):

namespace serde {    inline constexpr struct{} Serialize{};    struct rename { char const* field; };}
namespace boost::json { template <class T> requires (has_annotation(^^T, derive)) void tag_invoke(value_from_tag const&, value& v, T const& t) { auto& obj = v.emplace_object(); [:expand(nonstatic_data_members_of(^^T)):] >> [&]<auto M>{ constexpr auto field = annotation_of(M) .transform([](serde::rename r){ return std::string_view(r.field); }) .value_or(identifier_of(M));
obj[field] = boost::json::value_from(t.[:M:]); }; }}

這段代碼應(yīng)該看起來(lái)很熟悉,因?yàn)樗旧弦彩窃谧龈袷交ぷ?,只不過(guò)這里我們是將成員添加到一個(gè) JSON 對(duì)象中,而不是打印一堆鍵值對(duì)。然后,我們沒有自動(dòng)使用非靜態(tài)數(shù)據(jù)成員的標(biāo)識(shí)符,而是先嘗試檢查是否有 rename 注解。annotation_of()?為我們提供了一個(gè) optional,因此我們要么獲取 rename 注解的字段名(及其底層字符串),要么回退到 identifier_of(M)。

在這里添加 skip_serializing_if 的支持并不需要太多額外的工作,這也很好地展示了 C++ 和 Rust 處理方式之間的區(qū)別。在 Rust 中,你提供一個(gè)字符串,它會(huì)被注入并在內(nèi)部調(diào)用;而在 C++ 中,我們通常會(huì)直接提供一個(gè)可調(diào)用對(duì)象。

起初我以為這是因?yàn)?Rust 的屬性語(yǔ)法不支持在這里使用可調(diào)用對(duì)象,但實(shí)際上似乎是因?yàn)?serde 在支持這一點(diǎn)之前就已經(jīng)存在了。

我們需要為此添加一個(gè)新的注解類型:

namespace serde {      inline constexpr struct{} Serialize{};      struct rename { char const* field; };      template <class F> struct skip_serializing_if { F pred; };  }

然后稍微麻煩一點(diǎn)的部分是對(duì)它的解析,我們需要提取出某個(gè)?serde::skip_serializing_if?的特化類型注解。如果找到了,就嘗試調(diào)用其 pred 成員函數(shù),若該函數(shù)返回 true,就跳過(guò)該字段的序列化。

搜索過(guò)程如下所示(注意,我們需要使用 constexpr,因?yàn)樾枰唇铀赃M(jìn)行調(diào)用)。我確信這個(gè)部分可以通過(guò)更好的庫(kù) API 稍作清理(至少可以用一個(gè) std::optional 來(lái)改進(jìn)):

constexpr auto skip_if = []() -> std::meta::info {    auto res = std::meta::info();    for (auto A : annotations_of(M)) {        auto type = type_of(A);        if (has_template_arguments(type)            and template_of(type) == ^^serde::skip_serializing_if) {            // found a specialization            // but check to make sure we haven't found two            // different ones.            if (res != std::meta::info() and res != value_of(A)) {                throw "unexpected duplicate";            }
res = value_of(A); } }
return res;}();

然后,如果我們有這樣的注解,就調(diào)用它來(lái)確定是否需要跳過(guò)這個(gè)成員。這里需要用?if constexpr?語(yǔ)句,因?yàn)槿绻?skip_if?是空反射,我們無(wú)法對(duì)其進(jìn)行拼接。除此之外,整體邏輯非常簡(jiǎn)單:如果有這樣的注解,就調(diào)用它,如果返回?false,則跳過(guò)這個(gè)成員:

if constexpr (skip_if != std::meta::info()) {    if (std::invoke([:skip_if:].pred, t.[:M:])) {        return;    }}

現(xiàn)在這段代碼已經(jīng)膨脹到了 51 行(新增部分為第 7 行、第 22-46 行):

template <auto V> struct Derive { };template <auto V> inline constexpr Derive derive;
namespace serde { inline constexpr struct{} Serialize{}; struct rename { char const* field; }; template <class F> struct skip_serializing_if { F pred; };}
namespace boost::json { template <class T> requires (has_annotation(^^T, derive)) void tag_invoke(value_from_tag const&, value& v, T const& t) { auto& obj = v.emplace_object(); [:expand(nonstatic_data_members_of(^^T)):] >> [&]<auto M>{ constexpr auto field = annotation_of(M) .transform([](serde::rename r){ return std::string_view(r.field); }) .value_or(identifier_of(M));
constexpr auto skip_if = []() -> std::meta::info { auto res = std::meta::info(); for (auto A : annotations_of(M)) { auto type = type_of(A); if (has_template_arguments(type) and template_of(type) == ^^serde::skip_serializing_if) { // found a specialization // but check to make sure we haven't found // two different ones. if (res != std::meta::info() and res != value_of(A)) { throw "unexpected duplicate"; }
res = value_of(A); } }
return res; }();
if constexpr (skip_if != std::meta::info()) { if (std::invoke([:skip_if:].pred, t.[:M:])) { return; } }
obj[field] = boost::json::value_from(t.[:M:]); }; }}

此時(shí),我想到了解決此問題的另一種有趣方法。只有兩個(gè)屬性的時(shí)候這樣做可能沒有必要,但如果我打算實(shí)現(xiàn) serde 的全部功能,有一個(gè)不單獨(dú)處理每個(gè)屬性解析的策略可能會(huì)更加合理。那么,如果我們將所有屬性收集到一個(gè)類類型中,再使用這個(gè)類類型會(huì)怎樣呢?

讓我們看看這會(huì)是什么樣子。

首先,我們創(chuàng)建一個(gè)新的類類型——attributes。我們將編程定義它,給它一個(gè)每個(gè)屬性都對(duì)應(yīng)的成員,此時(shí)難點(diǎn)在于成員的類型。對(duì)于像 serde::rename 這樣的屬性,我們應(yīng)該使用 optional。但對(duì)于 skip_serializing_if 呢?我們還不知道該使用什么類型,所以這里先用?optional 來(lái)進(jìn)行類型擦除。也就是說(shuō),我們希望生成這樣的類型:

struct attributes {    optional rename;    optional skip_serializing_if;};

這段代碼使用了?std::meta::define_class(),這是 P2996 中唯一一個(gè)用于代碼生成的 API。它功能不多,但對(duì)當(dāng)前需求來(lái)說(shuō)足夠用了。注意,由于我們遍歷了命名空間?serde?中的所有成員,需要確保排除?attributes——它當(dāng)然也在這個(gè)命名空間中:

struct attributes;consteval {    std::vector<std::meta::info> specs;    for (auto m : members_of(^^serde)) {        if (m == ^^attributes or not has_identifier(m)) {            continue;        }
auto underlying = is_type(m) ? m : ^^std::meta::info; specs.push_back(data_member_spec( substitute(^^std::optional, {underlying}), {.name=identifier_of(m)})); }
define_class(^^attributes, specs);};

然后我們可以編寫一個(gè)解析函數(shù),將非靜態(tài)數(shù)據(jù)成員的屬性寫入 attributes 實(shí)例中。這里最麻煩的部分就是找到寫入 attributes 哪個(gè)非靜態(tài)數(shù)據(jù)成員。我們暫時(shí)跳過(guò)這部分邏輯,直接進(jìn)入如何利用這些工作成果:

namespace boost::json {    template <class T>        requires (has_annotation(^^T, derive))    void tag_invoke(value_from_tag const&, value& v, T const& t) {        auto& obj = v.emplace_object();        [:expand(nonstatic_data_members_of(^^T)):] >> [&]{            constexpr auto attrs = serde::parse_attrs_from();
constexpr auto field = attrs.rename .transform([](serde::rename r){ return std::string_view(r.field); }) .value_or(identifier_of(M));
if constexpr (attrs.skip_serializing_if) { if (std::invoke( [:*attrs.skip_serializing_if:].pred, t.[:M:])) { return; } }
obj[field] = boost::json::value_from(t.[:M:]); }; }}

當(dāng)然,我們將最復(fù)雜的邏輯(解析注解)移到了一個(gè)函數(shù)中,而這個(gè)函數(shù)我沒有包含在上面的代碼塊中。如我所說(shuō),對(duì)于只有兩個(gè)屬性的情況,這樣做可能有點(diǎn)大材小用。不過(guò),這種方法意味著添加一個(gè)新屬性只需在命名空間 serde 中聲明一個(gè)新類或類模板,然后在實(shí)現(xiàn)中使用它即可。


3、Rust 屬性 vs. C++ 注解

在對(duì)比 C++ 和 Rust 中的 serde 解決方案時(shí),有兩個(gè)方面引起了我的注意:語(yǔ)法和庫(kù)的設(shè)計(jì)。

語(yǔ)法

首先從語(yǔ)法差異來(lái)看,使用時(shí)的體驗(yàn)是我最先關(guān)注的點(diǎn)。以下是我在 Rust 中的聲明:

#[derive(Serialize)]struct Person {    #[serde(rename = "first name")]    first: String,
#[serde(rename = "middle name", skip_serializing_if = "String::is_empty")] middle: String,
#[serde(rename = "last name")] last: String,}

而這是我在 C++ 中的聲明:

struct [[=derive]] Person {    [[=serde::rename("first name")]]    std::string first;
[[=serde::rename("middle name")]] [[=serde::skip_serializing_if(&std::string::empty)]] std::string middle = "";
[[=serde::rename("last name")]] std::string last;};

可以看到,C++ 的注解語(yǔ)法顯得更為復(fù)雜和冗長(zhǎng),而這大多是由于語(yǔ)法本身的問題。相較之下 Rust 的注解較為簡(jiǎn)潔,因?yàn)樗鼈冏裱煌谡Z(yǔ)言其余部分的語(yǔ)法規(guī)則?—— 比如 serde(rename = "first name")?在 Rust 中是無(wú)效的,這里也沒有調(diào)用名為 serde 的函數(shù)。

這種差異帶來(lái)的好處是,Rust 中的注解使用起來(lái)更加清晰自然,因?yàn)樗娴木拖袷墙o選項(xiàng)賦值一樣。例如,類似于 serde(rename = "first name") 這樣的用法更像是傳遞配置參數(shù),而不是在調(diào)用函數(shù)。這為使用者提供了靈活性,比如可以像這樣使用屬性:#[arg(short)]?或 #[arg(short = 'k')],前者使用了默認(rèn)值,而后者顯式指定了 'k'。

看到這里,你可能會(huì)有一種沖動(dòng),想要重用(非常特殊且具體的)屬性語(yǔ)法,并允許在 C++ 中使用 using 關(guān)鍵字。但實(shí)際上,這樣做并不會(huì)節(jié)省太多的輸入:

struct [[=derive]] Person {    // old version: 83 chars    [[=serde::rename("middle name"), =serde::skip_serializing_if(&std::string::empty)]]    std::string middle = "";
// new version: 82 chars [[using serde: =rename("middle name"), =skip_serializing_if(&std::string::empty)]] std::string middle = "";};

相比之下,Rust 版本只有 74 個(gè)字符。雖然長(zhǎng)度上的差異并不大,但至少它少于 80 個(gè)。

另一方面,要關(guān)注 Rust 為實(shí)現(xiàn)這一點(diǎn)付出了什么代價(jià)。在 C++ 注解設(shè)計(jì)中,注解本質(zhì)上就是值。你需要學(xué)習(xí)的新語(yǔ)法很少,還可以很清楚地看到這里發(fā)生了什么。注解的內(nèi)容并不是由庫(kù)定義含義的咒語(yǔ),而是實(shí)際的 C++ 值。如果你不知道 serde::skip_serializing_if 是什么意思,可以直接查看它的定義。

你可能會(huì)注意到,在討論這些示例實(shí)現(xiàn)時(shí),我沒有提到如何從注解中解析出值——這是因?yàn)閷?shí)際上我不需要做任何解析,編譯器為我完成了這項(xiàng)工作!我唯一需要做的,就是從注解列表中提取我關(guān)心的注解,這并不涉及實(shí)際的解析過(guò)程。而 Rust 庫(kù)則必須真正解析這些 Token 流,對(duì)于 serde 來(lái)說(shuō),這意味著接近 2000 行代碼。

另一個(gè)有趣的事情是,盡管?Rust 和 C++?最終以不同的方式實(shí)現(xiàn)了相似的功能,但它們并不完全相同。在 Rust 中,#[derive(Debug)] 會(huì)為類型注入適當(dāng)?shù)?impl Debug。而在 C++ 的注解方法中,我們并沒有注入適當(dāng)?shù)?formatter 特化,只是添加了一個(gè)全局約束的版本。

這意味著,如果不做進(jìn)一步處理,僅僅做一個(gè)小小的改動(dòng)就可能導(dǎo)致歧義:

struct [[=derive]] Point {    int x;    int y;
// let's just make this a range for seemingly no reason auto begin() -> int*; auto end() -> int*;};
int main() { auto p = Point{.x=1, .y=2}; std::println("p={}", p); // error: ambiguous}

嗯,我需要做兩個(gè)小小的改動(dòng)。我原本的特化定義如下:

template <class T> requires (has_annotation(^^T, derive))struct std::formatter { /* ... */ };

但如果我將其改為:

template <class T, class Char> requires (has_annotation(^^T, derive))struct std::formatter { /* ... */ };

那么它就可能與 C++23 新增的用于范圍的 std::formatter 特化產(chǎn)生歧義。為了解決這個(gè)問題,可以禁用一個(gè)額外的變量模板(在鏈接中會(huì)被預(yù)處理掉):

template <class T> requires (has_annotation(^^T, derive))inline constexpr auto std::format_kind = std::range_format::disabled;

這似乎有點(diǎn)令人意外——因?yàn)閺母拍钌现v,C++ 的方法與 Rust 的方法是相同的,添加注解會(huì)注入一個(gè)非常特定且明確的特化,這不可能與其他內(nèi)容產(chǎn)生歧義——但事實(shí)并非如此。因此,這種部分特化的歧義肯定會(huì)成為一個(gè)問題。或許在未來(lái),我們可以想出一種方法,使諸如 [[=derive]] 這樣的注解能夠真正注入一個(gè)特化來(lái)避免這個(gè)問題。

庫(kù)設(shè)計(jì)

在 Rust 的 serde 庫(kù)中,序列化是一個(gè)兩階段的過(guò)程。首先,類型作者選擇參與序列化,這會(huì)生成一個(gè)類似于該類型即時(shí)表示的實(shí)現(xiàn)。然后,不同協(xié)議的作者可以有效地實(shí)現(xiàn)不同的后端。

例如在 Person 類型的 serde 實(shí)現(xiàn)中,Rust 會(huì)生成一個(gè) Serialize 實(shí)現(xiàn),該實(shí)現(xiàn)接受滿足 serde::Serializer 的任意類型。然后我們對(duì)這個(gè) serializer 進(jìn)行一系列的序列化調(diào)用,這些調(diào)用會(huì)根據(jù)協(xié)議需求(比如 JSON、CBOR、YAML、TOML 等)執(zhí)行相應(yīng)的操作。

如果我們將這種實(shí)現(xiàn)方式轉(zhuǎn)換為 C++,看起來(lái)可能會(huì)像這樣(為避免陷入不相關(guān)的錯(cuò)誤處理細(xì)節(jié),這里假設(shè)這些函數(shù)在出現(xiàn)錯(cuò)誤時(shí)拋出異常,而不是像 Rust 中那樣返回 Result):

template auto serialize(Person const& p, S& serializer) -> void {    auto state = serializer.serialize_struct(        "Person",        2 + (p.middle.empty() ? 0 : 1));    state.serialize_field("first name", p.first);    if (not p.middle.empty()) {        state.serialize_field("middle name", p.middle);    } else {        state.skip_field("middle name", p.middle);    }    state.serialize_field("last name", p.last);    state.end();}

這種設(shè)計(jì)允許解耦,非常不錯(cuò)。然而你可能注意到了,我之前展示的 C++ 實(shí)現(xiàn)根本沒有這樣做。并不是因?yàn)槲覒?,而是因?yàn)樵谟凶允。╥ntrospection)的情況下,這樣的操作完全沒有必要。在 C++ 中,我們不需要生成這種中間表示,Boost.JSON 實(shí)現(xiàn)可直接從數(shù)據(jù)成員完成所有的序列化工作。

這不僅僅是代碼量減少的問題,更重要的是根本不需要處理額外的抽象層。這個(gè)抽象層雖然不會(huì)消耗太多計(jì)算資源,也很容易被編譯優(yōu)化掉,但它本身就是不必要的。

接下來(lái)再考慮 skip_field 調(diào)用。對(duì)于很多序列化目標(biāo)(例如 JSON),跳過(guò)某個(gè)字段的方法就是簡(jiǎn)單地不對(duì)其進(jìn)行序列化。這也是為什么 skip_field 的默認(rèn)實(shí)現(xiàn)什么都不做,serde_json 也沒有覆蓋這個(gè)函數(shù)。同樣,考慮上面提到的字段數(shù)量計(jì)算。JSON 序列化器也不需要這樣的值,因此它會(huì)忽略這個(gè)字段類型名稱的值。

但在創(chuàng)建中間表示時(shí),你需要?jiǎng)?chuàng)建一個(gè)足夠豐富的表示來(lái)處理所有可能的序列化/反序列化目標(biāo)。某些序列化目標(biāo)可能需要預(yù)先知道字段數(shù)量,或者需要為跳過(guò)的字段預(yù)留位置。因此,serde 必須為此提供支持。

而在 C++ 中,我們根本不需要這樣做。對(duì)于任何給定的目標(biāo),序列化器可以直接執(zhí)行它所需的所有操作,因?yàn)樗梢灾苯釉L問所有信息,不需要額外的抽象層。因此,C++ 版本的 serde 庫(kù)可能只需要一系列可作為注解的類型、parse_attrs_from() 函數(shù),以及幾個(gè)小的輔助函數(shù)即可。


4、這并不是終點(diǎn)

最后,我想指出幾種不同語(yǔ)言中的一些相關(guān)特性來(lái)結(jié)束這篇文章:

  • Rust 的過(guò)程宏(procedural macros)

  • Python 的裝飾器(decorators)

  • Herb Sutter 的元類(metaclasses)提案

它們都有一個(gè)共同點(diǎn):編寫代碼,然后將代碼傳遞給一個(gè)函數(shù),以生成新的代碼。元類和裝飾器實(shí)際上會(huì)替換原始代碼,而 derive 宏只會(huì)注入新代碼(盡管其他過(guò)程宏也可以替換代碼)。

注解提案在大體上看起來(lái)與這些特性類似,但它是一個(gè)完全不同的機(jī)制,不應(yīng)與它們混淆:注解并不會(huì)注入代碼,它只是增強(qiáng)了類型的自省能力。但這并不是說(shuō)注解沒用!正如我所展示的那樣,注解有望成為一個(gè)非常有用的工具,可以編寫出以前在 C++ 中無(wú)法想象的用戶友好型庫(kù) API。

但這僅僅是一個(gè)開始。

本文轉(zhuǎn)自公眾號(hào)“CSDN”,ID:CSDNnews

本文經(jīng)授權(quán)轉(zhuǎn)自公眾號(hào)CSDN(ID:CSDNnews)

作者 | Barry Revzin,C++ 標(biāo)準(zhǔn)委員會(huì)成員,翻譯?| 鄭麗媛


EOF

你好,我是飛宇。日常分享C/C++、計(jì)算機(jī)學(xué)習(xí)經(jīng)驗(yàn)、工作體會(huì),歡迎點(diǎn)擊此處查看我以前的學(xué)習(xí)筆記&經(jīng)驗(yàn)&分享的資源。

我組建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起進(jìn)群交流。

歡迎你添加我的微信,我拉你進(jìn)技術(shù)交流群。此外,我也會(huì)經(jīng)常在微信上分享一些計(jì)算機(jī)學(xué)習(xí)經(jīng)驗(yàn)以及工作體驗(yàn),還有一些內(nèi)推機(jī)會(huì)。

加個(gè)微信,打開另一扇窗

經(jīng)常遇到有讀者后臺(tái)私信想要一些編程學(xué)習(xí)資源,這里分享 1T 的編程電子書、C/C++開發(fā)手冊(cè)、Github上182K+的架構(gòu)路線圖、LeetCode算法刷題筆記等精品學(xué)習(xí)資料,點(diǎn)擊下方公眾號(hào)會(huì)回復(fù)"編程"即可免費(fèi)領(lǐng)取~

感謝你的分享,點(diǎn)贊,在看三??

C語(yǔ)言與CPP編程 C語(yǔ)言/C++開發(fā),C語(yǔ)言/C++基礎(chǔ)知識(shí),C語(yǔ)言/C++學(xué)習(xí)路線,C語(yǔ)言/C++進(jìn)階,數(shù)據(jù)結(jié)構(gòu);算法;python;計(jì)算機(jī)基礎(chǔ)等
評(píng)論
  • 摘要防爆自控系統(tǒng)同時(shí)布設(shè)本安信號(hào)、工業(yè)總線、動(dòng)力供電三類線路,僅依靠標(biāo)識(shí)標(biāo)牌管控插接極易出現(xiàn)人為錯(cuò)插問題,高壓動(dòng)力回路竄入本安弱電回路會(huì)突破安全柵限能標(biāo)準(zhǔn),產(chǎn)生點(diǎn)火風(fēng)險(xiǎn)。依托 A、D、K 標(biāo)準(zhǔn)化機(jī)械防呆編碼體系,搭配護(hù)套顏色區(qū)分管理,適配 3 針 3 孔免焊接冷壓增安型防爆航插、5PIN 總線防爆航空插頭、8 芯焊接防爆法蘭插座的選型使用,從物理結(jié)構(gòu)層面杜絕防爆接近開關(guān)連接器、防爆傳感器航插接頭混用插接問題,補(bǔ)齊防爆系統(tǒng)被動(dòng)安全防護(hù)體系。關(guān)鍵詞圓形防爆連接器,防呆編碼防爆航插,防爆航空插頭,防爆
    kyfbest科迎法電氣 2026-07-31 18:29 425瀏覽
  • 在物聯(lián)網(wǎng)從“萬(wàn)物互聯(lián)”邁向“萬(wàn)物智聯(lián)”的進(jìn)程中,LTE Cat.1憑借其在速率、成本與網(wǎng)絡(luò)覆蓋之間的完美平衡,正成為中低速物聯(lián)網(wǎng)市場(chǎng)的絕對(duì)主力。隨著2G/3G網(wǎng)絡(luò)的逐步退網(wǎng)以及新國(guó)標(biāo)對(duì)終端設(shè)備智能化、定位功能的強(qiáng)制要求,Cat.1模組迎來(lái)了爆發(fā)式的增長(zhǎng)窗口。在這一關(guān)鍵節(jié)點(diǎn),基于華為海思Hi2131芯片設(shè)計(jì)的LTE Cat.1模組,憑借其在能效、射頻、架構(gòu)及物理規(guī)格上的底層創(chuàng)新,成功打破了“高性能必高功耗”的傳統(tǒng)魔咒,為行業(yè)提供了極具競(jìng)爭(zhēng)力的連接最優(yōu)解。核心技術(shù)優(yōu)勢(shì):重塑Cat.1能效與連接標(biāo)桿海
    用戶1778981989717 2026-08-05 21:04 184瀏覽
  • 產(chǎn)品使用18V供電,配套4串鋰離子電池,需要設(shè)計(jì)一個(gè)充電器。因?yàn)檩斎?8V,電池最高 16.8V,壓差僅 1.2V。因?yàn)閴翰钐 J褂瞄_關(guān)型的充電控制芯片不穩(wěn)定。嘗試自行設(shè)計(jì)一個(gè)。整體采用分立三極管 + TL431架構(gòu),分4個(gè)功能單元:使能控制回路、限流環(huán)路、TL431 精密恒壓反饋環(huán)路、PNP 功率管主功率輸出回路;實(shí)現(xiàn)完整鋰電池充電邏輯。使能控制回路按鍵按下:5V 電壓經(jīng) R12 (2kΩ) 送入 NPN 管 Q3 基極,Q3 飽和導(dǎo)通,對(duì)地拉低 R1、R2 組成的分壓控制節(jié)點(diǎn)電位;該節(jié)點(diǎn)直
    southcreek 2026-08-07 13:45 369瀏覽
  •     長(zhǎng)期專注WiFi領(lǐng)域,也算是行業(yè)老兵了!同行閑聊中,時(shí)不時(shí)能提起我和我們!從疫情起,國(guó)產(chǎn)替代風(fēng)生水起,起起落落,大浪淘沙,生存下來(lái)的都是強(qiáng)者!    WiFi模塊應(yīng)用廣泛,對(duì)于高端市場(chǎng)和特定市場(chǎng),要求WIFI模塊需要過(guò)CE/FCC之類的認(rèn)證,也就是說(shuō)沒有認(rèn)證的模塊,不具備導(dǎo)入產(chǎn)品設(shè)計(jì).接下來(lái)就和大家聊聊認(rèn)證模塊選型參考!    WIFi模塊硬件方面主要考慮以下九點(diǎn)特性:    1
    納拓科技 2026-07-31 17:56 284瀏覽
  • 在EMC測(cè)試過(guò)程中,傳導(dǎo)發(fā)射(Conducted Emission,CE)超標(biāo)是工程師最常遇到的問題之一。更令人頭疼的是,同樣的超標(biāo)曲線背后,可能對(duì)應(yīng)著完全不同的干擾機(jī)理:有的是差模噪聲沿電源回路傳播,有的是共模電流通過(guò)寄生路徑耦合,還有些問題甚至并非來(lái)自電源本身,而是負(fù)載、電纜布局或接地結(jié)構(gòu)引起的連鎖反應(yīng)。 · 150kHz~1MHz 超標(biāo):多為差模噪聲為主,干擾沿火線-零線回路傳輸。 · 1MHz~30MHz 超標(biāo):通常共模噪聲占主導(dǎo),干擾通過(guò)火線/零線對(duì)地回路傳播。 如果條件允許,用電流探
    譽(yù)磁電磁兼容EMC 2026-07-31 18:45 525瀏覽
  •        很多人只見過(guò)封裝在SMD外殼里的小尺寸晶振,卻不知道一顆2016、1612甚至1210封裝的微型石英晶振,從一塊巴掌大的石英原礦,到最終能穩(wěn)定起振的成品,要經(jīng)過(guò)十幾道精密工序。它內(nèi)部的石英晶片厚度最薄不到20微米,比普通A4紙的1/5還薄,整個(gè)生產(chǎn)過(guò)程里哪怕0.1微米的誤差,都會(huì)直接廢掉整批產(chǎn)品,這也是小尺寸晶振過(guò)去長(zhǎng)期依賴進(jìn)口,國(guó)內(nèi)廠商花了十幾年才啃下量產(chǎn)硬骨頭的核心原因。1. 原石定向切割:0.01度的角度誤差都不能有小尺寸晶振的第一步,
    TKD泰晶科技 2026-08-07 08:43 427瀏覽
  • 文:杜杰編輯:侯煜當(dāng)?shù)貢r(shí)間7月28日,美國(guó)特朗普政府落地最新科技封鎖政策,美國(guó)聯(lián)邦通信委員會(huì)(FCC)正式更新《受管制清單》,新增兩大類設(shè)備——境外生產(chǎn)的先進(jìn)機(jī)器人設(shè)備、境外生產(chǎn)的聯(lián)網(wǎng)電力逆變器。FCC在官方情況說(shuō)明書中明確,該類境外制造產(chǎn)品,“會(huì)對(duì)美國(guó)國(guó)家安全或美國(guó)民眾的人身安全構(gòu)成無(wú)法承受的風(fēng)險(xiǎn)”。本次新規(guī)設(shè)置明確豁免機(jī)制:經(jīng)美國(guó)國(guó)防部(DoW)、電力逆變器品類經(jīng)美國(guó)國(guó)土安全部(DHS)審核并授予“有條件批準(zhǔn)”,認(rèn)定對(duì)應(yīng)設(shè)備品類不存在重大安全風(fēng)險(xiǎn),即可獲得準(zhǔn)入豁免。同時(shí)政策嚴(yán)格執(zhí)行新老劃斷原
    華爾街科技眼 2026-08-03 21:28 349瀏覽
  • 摘要在防爆電氣設(shè)計(jì)中,只參照芯數(shù)選型而忽略額定載流量,長(zhǎng)期滿負(fù)荷運(yùn)行會(huì)出現(xiàn)插頭發(fā)熱、接觸面氧化、IP67 密封受熱老化等問題。結(jié)合常用 3 針 3 孔、4 芯、5PIN、8PIN 圓形防爆連接器,制作分級(jí)電流參數(shù)對(duì)照表,區(qū)分冷壓免焊接、焊接兩種接線結(jié)構(gòu)的安全載流數(shù)值,設(shè)置合理電流冗余系數(shù),搭配使用環(huán)境修正參數(shù),規(guī)范防爆接近開關(guān)連接器、防爆法蘭插座的選型計(jì)算方式。關(guān)鍵詞圓形防爆連接器,載流參數(shù)表,防爆法蘭插座,防爆端子接頭,IP67 防爆防水接插件1 產(chǎn)品概述圓形防爆連接器觸點(diǎn)載流能力由插針直徑、
    kyfbest科迎法電氣 2026-08-03 09:18 128679瀏覽
  • ?摘要:低價(jià)大流量卡正全面退市,政策收緊已成定局。面對(duì)傳統(tǒng)號(hào)卡的不確定性,隨身WiFi憑借穩(wěn)定連接、多設(shè)備共享、獨(dú)立設(shè)備和靈活套餐等優(yōu)勢(shì),成為務(wù)實(shí)可靠的備選方案。1、流量卡黃金時(shí)代要結(jié)束了通信行業(yè)正迎來(lái)一場(chǎng)前所未有的“地震式”調(diào)整——曾讓無(wú)數(shù)用戶追捧的低價(jià)大流量卡,正在全速退出各大平臺(tái)。這早已不是一次普通的競(jìng)合,而是一場(chǎng)趨勢(shì)性的行業(yè)洗牌?;赝^(guò)去,19元200G的神卡,那時(shí)真是“睡著都能笑醒”,便宜到不敢相信,爽到忍不住炫耀。那些日子,好像隨便刷、隨便看,流量自由觸手可及??扇缃瘢仡^再看,那種
    愛上電路設(shè)計(jì) 2026-08-03 10:21 128921瀏覽
  •                                                                        
    廣州鐵金剛 2026-08-06 17:44 160865瀏覽
  • 導(dǎo)語(yǔ)在現(xiàn)代高速數(shù)字電路設(shè)計(jì)中,信號(hào)的完整性已不僅是理想波形推演,而是一場(chǎng)與物理非理想特性正面博弈的工程實(shí)踐。濾波器芯片作為凈化傳輸鏈路的關(guān)鍵屏障,其核心挑戰(zhàn)在于:能否有效識(shí)別并濾除那些隱匿在電平跳變過(guò)程中的亞納秒級(jí)瞬態(tài)脈沖干擾,這類干擾持續(xù)極短,卻足以在高速鏈路中引發(fā)誤觸發(fā)。然而,傳統(tǒng)函數(shù)發(fā)生器受限于采樣率與邊沿速率,根本無(wú)法忠實(shí)復(fù)現(xiàn)如此狹窄且陡峭的異常波形。針對(duì)此困境,德思特TS-AWG7000系列任意波形發(fā)生器以17 GS/s采樣率與50 ps原生邊沿速度,為瞬態(tài)脈沖提供精細(xì)刻畫,配合SPG
    德思特測(cè)試測(cè)量 2026-08-05 14:06 5瀏覽
  • 在前六期中,我們已經(jīng)分別討論了 CRA 的適用范圍、整體影響、漏洞通報(bào)義務(wù)、風(fēng)險(xiǎn)評(píng)估與技術(shù)文檔證據(jù)鏈、第三方組件與 SBOM 供應(yīng)鏈治理,以及產(chǎn)品上市后的支持期、安全更新、用戶說(shuō)明和終止支持管理。到這里,企業(yè)已經(jīng)能夠回答幾個(gè)核心問題:產(chǎn)品是否落入 CRA 適用范圍,產(chǎn)品由哪些組件構(gòu)成,企業(yè)如何證明自己在設(shè)計(jì)、開發(fā)和上市前采取了必要的安全措施,以及產(chǎn)品上市后如何持續(xù)維護(hù)和安全下線。但對(duì)于真正準(zhǔn)備落地 CRA 合規(guī)的企業(yè)來(lái)說(shuō),還有一個(gè)更現(xiàn)實(shí)的問題:CRA 的法規(guī)條文相對(duì)原則化,企業(yè)應(yīng)當(dāng)如何把“網(wǎng)絡(luò)安
    虹科云科技 2026-08-03 16:51 209881瀏覽
  • 做藍(lán)牙音箱的工程師大概都碰過(guò)這個(gè)局面。產(chǎn)品要連手機(jī)放歌,要本地播開機(jī)提示音和按鍵音,還要接 App 做配網(wǎng)和狀態(tài)上報(bào)。三件事拆開,早先的常規(guī)做法是音頻藍(lán)牙一顆料、數(shù)傳藍(lán)牙一顆料、本地播放再掛一顆小 MCU 加一片 Flash。板子越畫越大,BOM 越算越貴,待機(jī)電流和固件工作量也跟著漲。WT2605C 出來(lái)以后,這套需求可以集成到一顆芯片,就是 WT2605C-32N,該芯片采用QFN32 封裝,4×4 毫米,把音頻藍(lán)牙、BLE 數(shù)傳和 MP3 本地播放三樣全包了。對(duì)藍(lán)牙音箱來(lái)說(shuō),最實(shí)在的好處就
    唯創(chuàng)知音語(yǔ)音芯片 2026-08-06 15:14 311瀏覽
我要評(píng)論
0
點(diǎn)擊右上角,分享到朋友圈 我知道啦
請(qǐng)使用瀏覽器分享功能 我知道啦
主站蜘蛛池模板: 阿凡提之奇缘历险| 3d柔铺团之极宝乐鉴宝鉴电影虞书欣 | 八尺夫人意大利免费观看完整版| 斯诺克虎牙4| 韩剧《女仆的秘密》全集播放| 火蓝刀锋2| 第一次遇见花香第二季| 和部长一起去出差旅的日子电影| 天地神剑电视剧在线观看| 二十世纪性格与X爱情观后感免费观看| 最近更新2019中文字幕免费| 天下无双张靓颖| 酒店第1-100集免费观看下载| 至尊无上之永霸天下| 杜拉拉升职记| 《部长夫人的美貌》的日剧叫什么 | 电视剧最后的较量| 加勒比海盗完整版1| 《暴劫倾情》在线观看| 玉蒲团高清| 女技师按摩前列腺电影| 想爱就爱在线视频| 云墨雨间电视剧免费观看 | 亲爱的在线观看| HD版《激战丛林》中文完整版| 小舍得电视剧全集免费| 金泽文子| 日本电影漂亮的售楼小姐| 吕良伟电影全集| 善良的岳母娘| 部长来家电影| 上流社会电影在线未删减| 电影《少爷与女佣》完整版| 电影《香醇的绣感》在线观看| 无处藏身全集电视剧免费观看| 欲罢不能阿拉丁版| 上门推销内衣女销售员2在线播放 美味快递员特殊待遇2在线观看免费高清 | 《家政老师2》完整版在线观看| 窈窕绅士演员表| 四格勇者| 邵氏《金莲外传》邵音音| 电视剧霸道总裁推荐| 铁石心肠第二部| 韩剧修理工的艳遇| 加勒比海盗3电影| 五行世家电视剧在线观看全集| 全红婵3跳满分名场面| 命中注定我爱你剧情分集介绍| 禁忌4年转一代禁忌| 磨刀不误砍柴工的意思| 鬼吹灯 黄皮子坟| 韩国电影公公的浮云之手| 无颜之月电视剧动漫在线观看| 花的勇气ppt| 豪放大兵1999电视剧免费观看全集意大 | 满天星版《荒岛女儿国》电影| 大都市小爱情 电视剧| 女篮决赛| 国产动漫| 运城市| 电视剧火线三兄弟在线观看| 洛丽塔1997| 渔夫的妻子意大利赛仑| 紫川第二季| 韩“慰安妇”想见佩洛西被警卫推倒| 《玉女心经3:阴阳和合》免费看叶子媚 | 东陵大盗电影全集| 兄弟的老婆5免费观看电视剧| 引诱我的巨乳女邻居| 与龙共舞电影| 贵妃还乡在线观看| 蜡笔小新免费观看| 四月女友| 恶魔法则下载| 行尸走肉第二季04| 日本电影和部长一起去出差旅的日子电影完整版 | 爱恋2015无减删高清| 李玟最火的一首歌| 游泳| 爱情真善美40| 绝地战警疾速追击| 忠奸人电影| 纸上谋杀| 美国版《健美》| 美丽人生百度影音| 为奴十二年| 欧美电影满天星萨迪萨默斯| 蜜桃成熟时33dqvod| 芳华在线| 需要爸爸播种2在线观看| 黑天鹅在线观看| 超感警探第四季| 美姐妹内牙医| 幻影忍者动画片| 恨透爱情恨透你| 高清平安夜,杀人夜| 半是蜜糖半是伤免费| 法国特殊理发店| 天城山奇案| 湘西诡事电影在线观看| 安斋拉拉电影免费观看高清版2023年| 地狱公使| 龙腾四海国语| 美国式禁忌保罗一家第三集| 我的丑娘续集| 《巡回检察组》在线观看| 迷奸家庭教师视频| 超级碗中场秀| 无限挑战110716| 《花与蛇9之终极阴谋》| 时寒冰经济大棋局| 新还君明珠| 40岁阿姨电视剧全集观看| 迪迦奥特曼国语版全集高清| 《姐妹2》完整版免费观看| 打脸前任全集免费| 三级剧情片| 突泉县| 朋友的妈妈2015在线观看| 异形:人族的统治| 坎贝奇第三部曲无憾免费播放| 新笑傲江湖花絮| 年轻的阿姨5中字电影| 变形金刚7免费完整版在线观看| 小舍得电视剧免费观看完整版| 火线警告第一季| 免疫屏蔽| 《3对1:两个人一次性体检》免费观看| 闪闪的红星在线观看| 创世纪1地产风云| 按摩四位少妇| 搞定岳父| 梦幻西游五庄观副本| 中字儿媳妇在线观看 | 至尊无上| 德古拉元年| 需要爸爸播种 - 在线追剧| 立体文眉| 高清《妻子》迷妹网| 黛比浪漫女家免费观看完整版| 死亡包裹| 高清索命舞娘未删减| 女朋友的妈妈费观看| 限制别人网速| 《金牌销售》法国| 假面骑士免费看| 家教reborn| 黑白配在线观看免费全集在线播放| 张惠妹演唱会2013| 张禧嫔电视剧| 彭水| 金装的维尔梅免费看全集| 另一个贝内特家的姐妹未删减| 长阳| 一代名妓陈圆圆| 你是我的荣耀在线观看免费完整版| 肥乡县| 左撇子女孩电影| 我的女婿太逆天电视剧| 邹城市| 樱花动漫鬼灭之刃游郭篇第五集| 《好久不见》韩剧在线观看| 万盛区| 《极恶老大》| 鬼话怪谈祥云寺| 称多县| 天堂口下载| 更好的生活| 你是我的氧气| 战狼6女版免费观看在线播放下载| 灾女变福妻:全家都宠我全集免费| 长歌行电视剧在线观看免费| 一个中国孩子的呼声教学设计| 刘大明白改命师全集| 瓦房店市| 大嫂查之女百度影音| 王牌保安电影| 电视剧潜龙行动| 女女免费观看高清完整版电视剧| 命运之夜前传| 入戏 曹格| 正在播放销售的销售秘密3-在线观看- 最新热门电影电视剧-麻花影院 | 逆世界在线观看| 大鱼电影在线观看| kendykross壮志凌云| 日本三国未删减| 魔帝归来:三界唯我独尊全集免费| jizz18日本| 拼命十三郎| 大神同学想被吃掉未增删有翻译| 《唤醒》:凯登·克罗斯| 庆余年2电视剧免费全集在线观看| 吵闹的邻居| 和大胸大乳继拇做爰| 封神演义评书| 下辈子做你的女人剧情简介| 《花子vs倔强驱魔师4》免费观看| 信义电视剧| 武林女大生| 性暴行| 爱情万万岁全集| 肖叮叮的剿匪记| 新金瓶梅电视剧免费观看全集完整版1986| 竹夫人完整版| 男子排队等升旗倒地睡着 | 王牌特工2:黄金圈| 公路美人 电影| 《星克莱尔总统夫人》电影观看| 青梅竹马是消防员全集| 洪金宝再谈周星驰| 哈利·波特电影全集免费观看| 酒店1-90集免费观看高凊| 师生畸恋下载| 金瓶梅1一5集高清视频手机免费 | 霍比特人意外旅程百度影音| 僵尸之地好看吗| 《秘密搜查官》免费观看| 海关战线 电影| 萧萧绝世唐门| 美容院5电影| 解放海南岛39| 儿童歌曲快乐的节日| 麦乐迪满天星在线观看| 昆仑神宫电视剧在线观看完整免费| 短道速滑王蒙| 房产销售的秘密5| 女人的战争之女人的理由| 深圳合租记| 阿凡达 国语| 《牝教师4~被玷污的教坛~》完整版免费在线观看_手机看牝教师4~被玷污的教 | 痛30集全集高清资源下载| 中国机长高清免费观看| 女人强奸| 公之浮手5免费观看完整版| 庆余年2在线播放策驰影院| 咱们结婚吧在线观看| 傲斗凌天v20| 女超人麦乐迪无删减版迅雷| 青海花儿胡汉三| 韩国综艺节目runningman| 俘虏之锁1| 波多野结衣全集线观看456| 白鹿原1-77集免费观看电视剧| 韩国电影想要爸爸一颗种子在线播放| 心动 电影| 我的错误在线观看高清| 推销避孕套被要求试用电怎么应对应| 裙子里面有里野兽| shameless第三季免费观看| 《水电工的艳遇》韩国| 暖暖 免费 在线 播放 中文| 中华英雄电影免费国语在线观看| 白鹿原未删减版| 韩国年轻大胸的继拇999| 我们一起来看流星雨| 蔷薇风暴电视剧免费观看高清| 五月深爱网| 女装正太 快播| 史诗电影| 锵锵锵锵游戏免费完整观看视频| 公主的诱惑| 超级战警高清| 农场保卫战| 《旁观者》| 有匪电视剧在线观看全集| 一代枭雄全集bt下载| 张云熙电影在线观看| 大话西游之月光宝盒电影| 迅雷电影网| 兄弟换麦6| 潜入调查官绝对不会输第2集| 庇护之地全集观看| 《金牌销售的秘密5》 | 1986版乌龙山剿匪记免费观看 | 日本动漫《花嫁高柳家》完整版电影 | 怎么办家康在线观看| 插曲的痛30全集免费观看高清版!| 守护天使国语版| 《与凤行》电视剧| 色影影院| 龚玥菲新金平瓶梅全集| 火箭军副司令吴国华昨晚自尽| 品味人生在线观看| 藏珠电视剧在线观看| 复仇者联盟字幕| 法国空姐免费高清原声高烧可以吃雪糕吗 | 鉴宝弃少:捡漏暴富全集免费| 锵锵锵锵锵锵锵锵电视剧免费观看| 萤火之森动漫| 胸肌撕裂者| 抗美援朝保家卫国纪录片第三集| 爱我几何电影完整版在线播放免费观看莫尼卡影院 | 新闻正前方| omoflow第一季真人版| 长津湖电影在线观看| 勇敢的心1| 肯德桑德兰满天星免费| 莫言作品丰乳肥臀| 乡村爱情故事第5部| 焦作市| 需要爸爸的播种 在线观看| 蓝可儿电梯事件| 西瓜视频在线观看免费| 渔女实战2免费观看正版电影 | 高清春日狂热未删减| 小沙弥欢喜看人间| 堕落的女律师电视剧免费观看| 渴望城市电视剧免费观看| 逆流而上的你免费观看完整版 | 绅探电视剧免费高清完整版| 尼泊尔新纸币激怒印度| 极恶非道 1| 巨茎挺进李淑芬的体 | 谈判专家2024在线观看| 午夜的太阳| 爱的初体验5电视剧全集在线播放 《被下药强辱的妻子》免费看 | 武藤兰av下载| 不能说的秘密mv| 凤隐电视剧免费观看完整版| 插曲的痛60全集免费观看无马赛克 | 在姨母家的客厅在线观看电影免费| 不够相爱的我们短剧| 高清《朱音落语》电视剧| 凤庆县| 彭丹徐锦江电影《悟间道之奇缘》| 精灵梦叶罗丽第四季| 电视剧京城四少| 美丽小蜜蜂4美国版| 犯罪图鉴电视剧| 八百壮士在线观看| 时间静止器电影免费观看完整版| 为爱疯狂| 还珠格格之燕儿翩翩飞| 黑帮大佬和我的365天 二集| 木子檀檀子剧免费观电影| 今夕是何年电视剧免费观看| 校草追求:学霸女神全集免费| 甜蜜第二季无马赛免费观看| 哪吒之魔童降世免费观看| 广场舞读西厢| 北京代驾| 韩国情趣胸部按摩电影| 苏格兰牧羊犬好养吗| 急先锋在线观看| 《新上任的秘书》| 驯龙记电影| 二十四小时日本中文在线观看| 电视剧如此婚姻| 特殊的快递员| 《难哄》全集观看免费高清完整版| 高清《戴拿奥特曼》免费观看| 双人调情按摩术韩国在线观看 | 恶魔阿萨谢尔在召唤你oad| 总会再相见全集观看| 市川真帆| 缘分五月在线播放| 隐形帽子在线观看| 名侦探柯| 寒战 电影| 最美和声第五期| 电视剧永不回头免费观看全集高清版| 韩国荷尔蒙爆发的视频原声| 高清《先婚后爱》网剧| 突然的喜欢演员表| 香港 三 电影| 大漠枪神全集| 插曲的痛30集全集免费播放| 九重紫在线电视剧免费观看完整版| 我们生活的年代 电视剧| 正在播放: ROE-417 在见证女儿幸福的同时……却在婚礼宴席上,被女婿侵犯的母 | 余罪电视剧第二季| 黑道风云二十年第二部| 龙之谷破晓奇兵2| 电影神奇女侠| 神话优酷| 《迪拜的女孩2满天星》在线观看 三年在线观看免费大全 | 他是谁全集| 刀郎颠倒歌| 野孩子美国电影高清| 日本又黄又爽gif动态图| 虫族之心剧情| 铿锵三人行| 3d肉蒲团之极乐宝鉴在线观看| 电视剧无忧渡在线观看| 埃及皇后2003| 超神学院第二集| 我是大歌神| 高清《献身》李丽珍免费观看| 一路向西是什么电影| 女朋友的妺妺HD| 女友的妈妈 双字ID| 普通的孩子未删减| 一本一道素人竹内纱里奈在线| 美国版荷尔蒙15| 韩国产理伦片在线观看| 好好疼在爱里5季免费观看 | 妈妈的男朋友完整版| 《小姨多鹤》电视剧| 满江红在线观看免费观看完整版| 《分娩按摩2》在线看| 陶笛天空之城| 布兰迪《不正当军事行为》| 高清望向太阳未删减| 《驯服小峓子》丽卡| 只有神知道的世界ii| 仁医11| 玄女心经2观看| 黄手帕国语版| 成熟玫瑰| 契约婚姻| 售楼员的秘密| 斗罗大陆51集| 男女愁愁愁全集共多少集 | 四大美人之西施高清完整版| 一起愁愁愁全集免费观看方法| 月上兔泰剧电视剧免费观看| 涡阳县| 喜羊羊与灰太狼勇四季城| 红楼残梦电影完整版免费观看| 韩国推油电影| 黑天鹅在线观看| 公妇借种林娇H| 《美丽妻子替夫还债》剧情| 舞台2023在哪个平台播出| 《和部长一起去出差旅》中文| 蓝色忧郁论坛| 法证先锋5| 电影《淫道》| 热带雨电影完整版免费看| 与贝拉1980相似的电影| 带队横扫全球:电竞女王全集免费| 仙逆59| 山东舰远海遭遇26架外军无人机| 电影《二十条》| 菜鸟老警 第八季全集观看| 美容美发店里的特殊待遇中文字幕| 高清《天气之子》电影免费看| 丽卡驯服2普通话版| 2对1:三人一次性体检韩国伦理| 恋恋红尘电视剧| 完美星球纪录片免费观看中文版| 《坏修女》免费观看高清电影 | 沧元图60完整版最新免费看| 只因单身在一起| 新平凡的世界电视剧| 21世纪性格爱情指南加拿大| 浅情人不知电视剧完整版免费观看| 动感之星视频全集观看| 花与蛇在线看| 朝九晚五帅气和尚爱上我全集| 为爱所困电视剧| 电视剧记忆之城| 啄木鸟版《灰姑娘》演员表| 躺过男人河的女人| 我的女孩主演| 妻子日本电影| 魔女2在哪里能看| 马达加斯加的企鹅第二季| 高清致命录像带:万圣节| 好想好想谈恋爱电视剧| 公浮手中字8| 日出免费| 公主岭市| 东京猫猫全集| 唱k小鱼仙| 战狼6免费完整电影在线观看高清视频软件下载 | 公之浮之中字5| 雪迷宫电视剧在线观看| 电竞大神:带飞全场超酷飒全集免费 | 高清《女生宿舍2》| 公之浮之中字5hd年纪大| 剃须然后捡到女高中生女主| 出差偶遇前男友| 电视剧蛮好的人生免费观看全集高清| 幸福三重奏第二季| 加勒比女海盗1在线观看| 披荆斩棘第4季在线观看| 插曲的痛免费播放完整中国| 男子地铁上扇女子脸质问爱谁| 《长安三万里》免费观看超清画质| 高清年少有为| 高清《女囚》电影| 凯登克罗斯大合集在线观看免费全集| 山西电视台少儿频道| 小汤圆正式出院| 刀手普通话版国语版| 知源中医| 24世纪性格爱情指南大结局 | 《法国空乘11》播放| 甄嬛传45| 中字年轻漂亮的儿媳BD| 朴亚珍在线电影| 《妻子7》在线观看全集| 古墓丽影满天星高压美版免费| 低俗小说电影| 唐朝艳史| 电影美乃雀主演的电影在线观看| 灭火女孩在线观看完整免费高清原声高烧可以吃雪糕吗 | 杨思敏1996版电视剧全集| 蜜桃5电影| 福宝空间短剧全集| 二连浩特吧| 与小仙女混居的日子| 浪蝶狂花| 韩国维修师的艳遇BD金山丽| 流浪地球1免费观看完整版| 笑功震武林粤语百度影音| 捆绑堵嘴3级强奸电影| 赤井美月| 俄罗斯女超人| 何洁门照艳全集| 《维修工的艳遇2》| 后宫甄嬛传全集高清| 火线警告第一季| 激战丛林在线播放| 夫妻成长日记百度影音| 大竹县| 爱情最美丽演员表| 连锁反应英文版| 爱我几何原版无删减版库拉格| 边防风暴| 《入室暴行》国语在线| 迷航昆仑墟| 调音师电影在线观看免费版 | 荆棘之恋泰剧免费播放| 女子特案组全集| 今天开始恋爱吧| 欢乐家长群2电视剧观看 | 麻将电影| 憎侣之夜| 咒术回战| 我的1997免费观看完整版| 啄木鸟满天星电影壮志凌云在线观看| 琉璃电视剧在线观看免费全集| 我在岛屿读书第一季| 少年男仆库洛下载| 新逃学威龙张浩在线观看| 飞机上的特殊服务电影| 我在他乡挺好的一共多少集| 薛丁山演员表全部| 年轻的女学生2| 完美无瑕电影| 欧式少女全集观看| 程凯博士| 迷你世界全皮肤破解版下载| 花姑娘HD| 动画乐翻天| 一夜惊喜电影| 泰国鬼片《黑煞女》在线观看免费 | 黑洞频率| 法国酒店实生3完整版| 情满雪阳花电视剧全集| 乡下大叔成为剑圣| 东京塔之越南战俘营完整版电影在线观看 | 暴操老妈1一46集免费看| 青楼十2房在线观看| 《推拿男医生》免费观看全集| 新五女拜寿演员表| 快播成电影人网站se| 369你懂的电影在线视频| 影子爱人电影| XL司令的动漫在线观看免费| 符文密码| 小蜜蜂电影| 韩国理伦片驯服小峓子同男主| 少年高潮H跪趴扩张H| 黑帮大佬和我的365| 法国版《女超人:麦乐迪》在线观看中文| 兄弟之道在线观看高清免费| 特殊的保险推销员2中国| 河南四平调小包公| 故乡的泥土一共多少集| 俄版《女儿国》满天星在线观看 | 一个软妹三个糙汉| 老虎出更国语| shameless第三季免费观看| 塔日酒店电影免费观看高清版 | 要爸爸播种| 不雅视频完整版| 冲动的惩罚未删减动漫| 丈夫的属下中字2| 小日子在线观看全集免费高清版| 成全电影大全在线观看第二季 | 天使与龙的轮舞 在线观看| 食来孕转电视剧| 德国中产家庭排7小时队领救济粮| 3d肉蒲团图片| 电影《美味快递员》演员阵容| 狼人大战| 鉴宝弃少:捡漏暴富全集免费| XL司令第二季真人版在线观看| 智取威虎山电影| 要过好日子全集| 电影爱我几何在线观看| 液晶电视安装| 捆绑电影| 特殊按摩3免费观看| 我的姐姐很逆天短剧| 宇崎学妹要想玩| 需要爸爸的种子在线美国观看| 蜜桃成熟时电影观看| star472| 都市巡警| 电梯故障2| 麦乐迪版满天星无删减观看 | 麦乐迪与父亲家庭矛盾在线观看| 北京遇上西雅图 影音| 花千骨全集未删减版在哪里可以看| 木子檀檀子剧免费观看| 少帅的秘密情人| 木下檀檩子在线| 幸福选择题下载| 廊桥遗梦电影| 年轻的大胸继拇日本| 飞越疯人院电影| 重新爱上你泰剧在线观看| 贝拉1980满天星版| 最高の爱人诏电影| 你到底想要什么电影在线观看| 《保罗1980》完整版| 电视剧奇志| 电视剧民工全集| 牙医姐妹郝板栗1986| 快乐星球第五部| 高清《建军大业》电影高清在线观看| 《驯服小峓子2》| 好男人手机在线视频播放1| 快乐到死电影完整版| 妖艳女忍者传紫雨篇| 私藏浪漫在线观看| 总统之夜1997满天星免费观看| 北风那个吹在线观看免费完整版 | 日本人已经不吃海鲜了| 部长来我家日本电影| 高清《失窃的女孩》电视剧| 精灵俏女巫| 在线看《美容院的特殊待遇》| 计春华简介| 泰秘丽人会所| 母亲爱上儿子同学日本在线| 娇妻互换我的漂亮娇妻6| 《女销售员的销售秘密2》| 电视剧贫嘴张大民| 出轨的女人粤语| 法国空姐4在线| 每年卖不完的月饼都去哪了| 《梦境》电影| 女销售| 金瓶莲手机免费看| 电视剧永不回头免费观看全集高清版| 美国《无憾》坎贝奇| 爱情真善美电视剧| 终极证据电视剧| 屠场呕吐娃娃免费高清观看完整版| 泰剧折翼天使| 僵尸道长2 电视剧| 人猿泰山电影1995意大利| 天使降临| 玛丽娜 阿布拉莫维奇| xl司令在线观看全集免费| 需要浪漫11| 母亲5免费完整高清电视| 黑白配国语高清在线| 5G天天奭5G天天运动快点播| 妻子的谎言 电视剧剧情免费观看| 灭火宝贝2高清在线播放完整版 | 开心家族下载| 《越南暴行2》在线观看| 变身动漫| 咋们结婚了吧| 地爆天星| 我是特种兵3全集| 单身即地狱第三季在线观看| 和部长一起出差的日子| 爱情 电影 在线观看| 单恋双城粤语版电视剧在线观看| 本乡爱,二阶堂梦| 如意芳霏在线观看| 郝蕾《颐和》片段高清| 菲律宾电影《健身房教练》的导演是 | 需要爸爸播种籽第二季| 过年回家| 爱上直播军事| 尹世娜《二对一》男演员| 怪想售动漫| 大鱼海棠湫| 黑暗侵袭3全集高清免费播放| 聊斋艳谭全集在线观看| 父亲筷子兄弟电影| Overfloor第一季在线观看| 台媒高度关注东部战区军演| 饥饿游戏2:星火燎原| 秘密女搜查官輪の姦組| 电影《猎金游戏》在线观看| 咒术回战| 特种部队2 全面反击国语版| 美景之屋6普通话版百度云 | 哇嘎免费播放完整版| 奥尔多占星者| 第九区在线观看| 萨迪1980满天星免费播放| 春来定风波大结局| 蜘蛛侠之英雄无归| segege| 南泥湾简谱| 好久不见电视剧在线观看免费完整版| 战狼6在线观看完整免费| 为了奥运会巴黎地铁椅子终于洗了| 《乡村爱情16》电视剧| 临湘市| 密爱电影| 计春华简介| 刀锋1937| 空姐8| 与狼共舞电视剧40集| 索命邀请函| 公的浮之手中字40| 明日战记电影免费观看完整版| 无影灯下连续剧全集免费播放| 清丰县| 按摩胸部 电影| 电影铁皮鼓| 善良的嫂嫂2| .奇怪的美容院2| 闯关东李幼斌版| 鄄城县| 阴阳尼姑2免费看| 咏春大师被ko| 买年货小品| 隔山有眼3| 向前吧少年| 高校教师成熟在线播放视频| 女销售的秘密2| 搞笑漫画日和中文| 亡国公主短剧全集| 7巴字开头中字的| 王龙妻子称要把慰问金都给公婆| 明星的恋人电视剧| 时间静止器电影韩剧免费观看| 绝密543电视剧在线观看全集免费| 千山暮雪百度影音| 神奇动物2格林德沃免费观看| 翻斗鞋柜的安装视频| 美丽教师未删减完整免费观看| 奥特曼国语全集| 安吉拉·怀特 透视| 婆婆来了剧情| 流量女王短剧全集| 天海翼女教师无删减版| 上流复仇| 《流光引》| 昭觉县| 销售的销售秘密4中字| 血腥的夜晚| 一路向北全集免费观看| BBBwww| 《卿卿我心》电视剧免费观看| 贺军翔主演电视剧| 爱情悠悠药草香全集| 《美容院待遇》酷客| 一步之遥电影下载| 恋爱之瘾台湾剧免费播放在线观看| 郝板栗《千金》免费观看| 沐浴之王演员| 一起来看流星雨在线观看免费高清 | 问题少女电影| 坎贝奇无憾完整版免费观看| 当着老公的面被搬运工在线| 铁血战士3| 隐形帽子在线观看| 马达加斯加2高清| 以家人之名更新| 360大佬爱上我第二季免费 | 密室大逃脱第六季免费| 猫和老鼠东北方言版| 天生反骨| 韩国 奇妙的发型屋| 罪与罚作者| 流浪地球2| 退钱哥又让退钱了| 满天星古墓丽影在线观看完整版| 男子私藏枪支家中宛如“军火库”| 废柴天才:召唤超神短剧全集| 风调雨顺歌谱| 销冠的秘密2| 日日艹夜夜艹| 囧妈初一上线免费播出| 三进山城在线观看| bf-597 丈夫不在的四天 中文字幕| 小泉今日子| 神偷奶爸3(普通话)| 好好生活电视剧免费观看| 保姆的特别待遇2| 赤板栗牙医姐妹1986在线观看| 鬼吹灯之精绝古城2| 慈禧秘密生活高清完整电影星辰 | 战地花开| 甲午风云电影| 牌坊下的女人插曲| 我的学妹| 魔镜号在线观看完整版免费 | 西关大少粤语高清| 美剧我要爸爸的种子| 《需要爸爸播种子》电影免费在线观看,高清完整版美国倫理片_屠夫电影网 | 电视剧西施秘史全集| 女超人满天星版麦乐迪完整版| 《催眠项圈》完整动漫在线观看| 与黑帮大佬的365日| 银魂176| 巨胸护士风间由美播放| 极品女士第三季| 杀生在线观看| 前列腺之家| 德川幕府三部曲水月篇| 电影《魔女3》免费观看国语| 求求鬼别吃我| 家有大姐| 南溪县| 束缚游戏免费观看第一季全集| 欢乐家长群第一季| 电视剧别对我说谎| 神印王座免费动漫在线观看| 《分娩按摩》免费观看高清| 王多鱼打扑克牌免费视频 | 原始武器 国语| 疏勒县| 肢体的绣感在线看完整版免费观看 | 电影需要爸爸播种子| 木下电影电影免费播放| 宝莲灯后传1到46集免费| 实习医生电影| 马与人电影在线观看免费播放| 女人公敌电影| 电影《青蛇》在线观看| 私人女性監獄| 麦乐迪马克斯女超人[玫瑰]| 《雪中悍刀行》第2部| 世界十大巨兽排行榜| 画满一张纸| 变形金刚1电影下载| 男按摩师| 四个少妇按摩记A片| 锦绣安宁电视剧免费观看全集高清| 庆余年2免费高清观看西瓜| 大唐好男人电视剧| 创造营亚洲第二季| 年轻的阿姨6有限公司中字974| 女性私人监狱| 《混血儿的摇篮曲》1973| 电影三毛从军记| 演员赵立新现状|