递归之美 - Loki库TypeList源码剖析

【C++内存管理】loki::allocator 源码分析 loki 是书籍 《Modern C++ Design》配套发行的一个 C++ 代码库,里面对模板的使用发挥到了极致,对设计模式进行了代码实现。这里是 loki 库的源码。 ps. 有空是不是应该把里面的设计模式的代码学习学习, hahahah loki 库里面有两个文件,SmallObj.h 以及 SmallObj.cpp,就是一个内存管理的内,可以单独使用。下面就其源码进行分析。 1. 类层次结构 SmallObj 文件里面有三个类:Chunk,FixedAllocator 和 SmallObj 阅读详情
 

递归之美 - Loki库TypeList源码剖析

邓 辉

 

TypeList概观

提起List,想必大家都不会陌生,它是一个元素的集合,并且提供了一些对该集合进行操作的方法,比如:计算集合中元素的个数、向集合中添加元素、获取给定索引处的元素等。我们所熟知的List中的元素一般都是实例化后的值,相关的操作也都是在运行期间进行的。本文将要剖析的List和上述的List在某种意义上比较相象,不过它所包含的元素都是类型(type),相关的操作是在编译期间进行的。

本文将要讲述的TypeList取自Andrei Alexandrescu的力作《Modern C++ Design》一书相关的Loki库,关于《Modern C++ Design》,C++的爱好者想必不会陌生,在该书中,作者向我们展现了C++设计的全新思维,把C++的表达能力发挥到了极至,而Loki库正是这种思维的具体表现。TypeLsit是Loki库中最为基础、核心的组件,理解了TypeList就具备了观赏这道C++新景观的基础。

下面我们先来看看如何定义一个TypeList,这样可以对于TypeList先有一个感性的认识。

typedef TYPELIST_3(char, int, long) MyTypeList;

定义了一个具有三个元素的TypeLsit,这三个元素分别为:char、int、long。TYPELIST_3为Loki库中提供的用于定义TypeList的工具,我们会在本文的后面进行介绍。

::Loki::Length ::value;

计算MyTypeList中元素的个数,结果为3。

typedef ::Loki::TypeAt ::Result MyType;

获取MyTypeList中第1个元素(从0开始),此时MyType就是int。

typedef ::Loki::Append ::Result MyTypeList1;

向MyTypeList中在添加一个元素:float,结果为MyTypeList1。此时::Length ::value 等于4。

好了,先介绍这么多,下面我们将介绍TypeList实现的一些相关的背景知识,包括:递归的基本概念、模板的特化(tempalte specilization)、模板的偏特化(template partial specilization)以及类型萃取技术(type traits)。

TypeList相关技术

递归概述

对于递归,大家肯定都不陌生,使用递归方法给出的解决方案总是显得非常的优雅、简洁。不过递归方法所适合解决的问题应该符合下面的条件:

    1. 一个问题的解决依赖于一个较小规模的同样的问题的解决

    2. 必须有一个明确的结束条件

    3. 这个结束条件是可达的

如果一个问题符合上述的三个条件,我们就可以使用递归的方法。首先我们定义一套解决问题的规则,接着缩小问题的规模并应用同样的规则直到达到结束条件,然后结果层层返回直到原始问题。著名的汉诺塔问题就是一个典型的递归问题,如果不使用递归方法,解决汉诺塔问题就会显得非常的复杂,晦涩。

我们在使用递归方法设计程序时,这个递归过程的调用总是在运行期间进行的。本文所介绍的TypeList的实现中,递归的执行是在编译期间进行的,那么在编译期间如何定义每次递归的返回结果,如何定义结束条件呢?其中主要使用了下面将要介绍的模板特化、偏特化以及类型萃取技术。

模板特化和偏特化(template specilizaiton、partial specilization)

什么是模板的特化、偏特化呢?大致的意思为:如果一个template拥有一个或者一个以上的template参数,我们可以针对其中一个或者多个参数进行特化处理(如果全部进行特化处理就是全特化,否则就是偏特化,切记:函数模板只能进行全特化,不能进行部分特化)。也就是说,我们可以提供一个特别版本,符合泛化条件,但是其中某些(全部)template参数已经由实际类型或者数值取代。

假设我们有一个class template定义如下:

template

class C { … };

对于模板的偏特化,刚刚接触可能会存在一些误解:以为模板的偏特化版本一定是对template参数U或者V或者T(或者它们的任意组合)指定某个参数值。其实不是这样的,所谓模板的偏特化是指另外提供一份template的定义,它的具体含义可以和通用的template定义版本无关。在一个偏特化版本中,template 参数的个数并不需要吻合通用的 template 中的个数。然而,出现於于class 名称之后的参数个数必须吻合通用的 template 的参数个数。下面举一个简单的例子进行说明:

template

class C { … };

这个泛型版本允许T为任意的类型。它的一个偏特化版本如下:

template

class C { … };

这个偏特化版本仅适用于T为原生指针类型的情况。

当我们使用C 去定义一个变量时,编译器会自动使用泛型版本,如果使用C 去定义一个变量时,编译器就会自动使用偏特化的版本。有了这个利器,我们就可以解决在编译期间定义递归的结束条件的问题。

类型萃取(type traits)

类型萃取技术是泛型程序设计中的一个常用技术,它的思维核心为:把一系列与类型相关的性质包裹于单一的class 之内,这样我们就可以在编译期间获取一些所需要的和该类型相关的东西。其实这个思路就是软件领域一句著名的谚语:“任何事情都可以通过添加额外的中间层次得以解决”的又一次体现。通过把一系列想得到的类型相关的信息封装在另外一个类型定义中,这样就可以以一致的方式来对这些类型进行处理,提供了强大的可复用性和灵活性。

类型萃取技术一般都和模板的特化、偏特化技术结合在一起运用,这样它们就可以互相补充发挥出巨大的威力。下面简单举一个例子来了解一下类型萃取技术。

我们来看看Boost库中一个简单的template class is_pointer 的实现。我们需要一个主版本,用来处理T不为指针的所有情况,以及一个偏特化版本,用来处理T是指针的情况:

template

struct is_pointer

{ static const bool value = false; };

template

struct is_pointer

{ static const bool value = true; };

一个简单的示例如下:

template

void Func(T param)

{

if (is_pointer ::value) {

// do something

}

else {

//do something

}

…

}

通过类型萃取技术,我们就可以在编译期间保留每次递归的结果,供递归返回时使用。

关于这些技术的更多、更深入的介绍,请读者自行参考相关资料,不在此赘述。在下面的源码剖析中,读者将会看到这些技术的实际运用。

TypeList实现剖析

有了上述的背景知识,下面我们就来揭开TypeList的神秘面纱,走进TypeList的源码世界。首先,我们来看一下TypeList的定义。

TypeList定义

为了能够一致的进行TypeList的操作,在Loki库中定义了一个空类型NullType来标记一个TypeList的结束,NullType和TypeList的定义如下:

class NullType { };

template

struct Typelist

{

typedef T Head;

typedef U Tail;

};

对于规范型TypeList的定义采用了一种尾递归的方法:

    1. NullType是规范的TypeLsit

    2. 如果T是规范的TypeList,那么对于任意原子类型U,TypeList,T>是规范的TypeList

Loki库中所采用的TypeList均为规范型的TypeList,这样可以在不减少灵活性的前提下简化对于TypeList的操作。本文后面所指的TypeList均为规范型的。

如何定义一个TypeList呢?比如:包含:char、int、long三个元素的TypeLsit。根据上面的定义,可以得到如下的定义形式:

TypeList > >; // 注意两个>间一定要加一个空格,不

// 然编译器会认为是 “>>” 操作符

这样的定义方法显得比较麻烦、罗嗦,为了简化用户对于TypeList的使用,Loki库中采用了宏定义的方式对于大小在1~50的TypeList进行了预定义:

#define TYPELIST_1(T1) ::Loki::Typelist

#define TYPELIST_2(T1, T2) ::Loki::Typelist

#define TYPELIST_3(T1, T2, T3) ::Loki::Typelist

…

依此类推。

这样用户在使用起来就会比较方便一些。

TypeList典型操作实现

了解了TypeList的定义,这里我们将对于TypeList相关的三个典型操作(Length、TypeAt和Append)的实现进行详细的剖析。掌握了这几个典型的操作,再学习其他的操作就会变得非常的容易。

我们将通过一个实例进行讲解,来看一下编译器实际的运作过程。我们定义了一个包含两个元素:int以及long的TypeList。

typedef TYPELIST_2(int, long) MyTypeList;

此时,编译器会根据TypeList的定义方式产生如下的类型定义结果:

struct TypeList

{

typedef long H;

typedef NullType T;

};

struct TypeList >

{

typedef int H;

typedef TypeList T;

};

 

Length的实现 - 获取TypeList中的元素个数:

template struct Length; // 仅有声明,没有实现,如果所传入的类型不是TypeLsit

// 的话,会产生一个编译期错误

template <> struct Length // 递归调用的结束条件,NullType的大小为0,运用了

{ // 模板特化和类型萃取技

// 术

enum { value = 0 };

};

template // 递归的规则定义,运用了模板偏特化和类型萃取技术

struct Length< Typelist >

{

enum { value = 1 + Length::value };

};

 

当通过Length ::value 获得MyTypeList中的元素个数时,看看编译器是如何根据我们指定的规则进行递归调用的。首先编译器会生成如下几个版本的Length定以:

struct Length >

{

enum { value = 1 + Length ::value };

};

struct Length > >

{

enum { value = 1 + Length >::value };

};

根据Length结束条件的定义可知,Length ::value 等于0,所以Length >::value 就等于Length ::value+1 ,也就是1。通过递推可知,Length ::value 也就是Length > >::value 等于Length >::value+1 ,也就是2。在层层的递推过程中,类型萃取技术得到了充分的体现,value就是我们想要得到的TypeList类型相关的信息,在每一层的递归过程中,都是通过它来保留结果的。

TypeAT的实现 - 获取给定位置处的元素

template struct TypeAt; // 仅有声明,没有实现,如果所传入

// 的类型不是TypeLsit

// 的话,会产生一个编译期错误

template

struct TypeAt , 0> // 递归调用的结束条件,如果给定位置为0,则

{ // 返回TypeList中的第一个元素

typedef Head Result;

};

template // 递归规则定义,注意这里的返回结果为类型,

struct TypeAt , i> // 运用了类型萃取技术。typename关键字的作

{ // 用是告诉编译器其后的实体是类型。

typedef typename TypeAt ::Result Result;

};

下面看看使用TypeAt ::Result 时,编译器都产生了那些动作。首先编译器要根据递归规则生成如下的类型定义:

struct TypeAt , 0>

{

typedef long Result;

};

struct TypeAt > , 1>

{

typedef TypeAt , 0>::Result Result;

};

很明显typedef TypeAt , 0>::Result Result; 就是typedef long Result;所以,TypeAt ::Result 就是long类型。同样的,在这个实现中充分使用了类型萃取技术,不过,这里我们想要的不是value,而是Result。

 

Append的实现 - 在TypeList的末尾添加一个元素

template struct Append; // 仅有声明,没有实现,如果所传入

// 的类型不是TypeLsit

// 的话,会产生一个编译期错误

template <> struct Append // 递归结束条件定义,模板的偏特化

{

typedef NullType Result;

};

template struct Append // 递归结束条件定义,模板的偏特化

{

typedef TYPELIST_1(T) Result;

};

template

struct Append > // 递归结束条件定义,模板的偏特化

{

typedef Typelist Result;

};

template // 递归规则定义,注意这里的返回结果为类型,

struct Append , T> // 运用了类型萃取技术。typename关键字的作

{ // 用是告诉编译器其后的实体是类型。模板的偏特

// 化

typedef Typelist

typename Append ::Result>

Result;

};

同样的,让我们来看看当使用Append ::Result 时,编译器的递归执行动作。首先看看编译器会生成的一些类型定义:

struct Append

{

typedef TYPELIST_1(float) Result;

};

struct Append , float>

{

typedef TypeList ::Result > Result;

};

struct Append >, float>

{

typedef TypeList ,float >::Result > Result;

};

经过简单的替换,Append ::Result 就等于:

typedef TypeList ,float >::Result >;

等于:

typedef TypeList ::Result > >;

等于:

typedef TypeList > >;

也就是:

TYPELIST_3(int, long, float) ;等同于在原有TypeList的末尾添加了一个新元素float。不用多说了,这里类型萃取技术同样发挥了巨大的作用。

 

结束语

本文对于TypeLsit的实现进行了剖析,相信读者朋友对于TypeList含义以及实现手法已经有所掌握。那么TypeLsit到底有什么用处呢?源码面前,了无秘密,掌握了TypeList,就掌握了全面理解Loki库的钥匙。在Loki库中,你将会看到Abstract Factory模式、Visitor模式这些泛型组件是如何在TypeLsit的基础上搭建起来的。背起你的行囊,拿起这把钥匙,赶快踏上你的“寻宝”之路吧(Loki库的源代码可以从www.moderncppdesign.com下载)。




内存分配(4)–LOKI的小物件分配器 <br /> 今天我们来看看Loki的内存分配.有一本书叫做<Mordern C++ Design>,个人认为是一本非常不错的书,值得一读。Loki的内存分配目的很明确,loki的设计者认为当前的C运行库的内存分配函数(malloc/realloc/free)并没有针对小内存分配做过优化,导致在有大量小内存频繁分配释放的环境下性能很差,所以他要解决这个问题,这一点倒是和C++ STL很相似,只不过loki的设计者更露骨,他在给分配器起名字的时候也充分强调了loki的内存分配是针对小物件做了大量工作的 阅读详情

相关推荐

loki 库 源码 策略设计模式

loki库采用了高强度的c++模板设计,能充分的体现基于策略的设计模式。

C++库之一:Loki

Loki 是一个强大的 C++ 模板库,专注于提供设计模式、元编程和灵活的内存管理工具。它适合需要高性能和复杂设计的项目,并且其轻量级和模板化的特性使其非常灵活。

qq_39437730的博客 1456

Loki库源码(泛型模式C++库)

Loki is the name of a C++ software library written by Andrei Alexandrescu as part of his book Modern C++ Design.   The library makes extensive use of C++ template metaprogramming and implements several commonly used tools: typelist, functor, singleton, smart pointer, object factory, visitor and multimethods.   Originally the library was only compatible with two of the most standard conforming C++ compilers (CodeWarrior and Comeau C/C++): later efforts have made it usable with a wide array of compilers (including older Visual C++ 6.0, Borland C++ Builder 6.0, and GCC). Compiler vendors used Loki as a compatibility benchmark, further increasing the number of compliant compilers.   Maintenance and further development of Loki has been continued through an open-source community led by Peter Kümmel and Richard Sposato as a Sourceforge project.Ongoing contributions by many people have improved the overallrobustness and functionality of the library. Loki is not tied to thebook anymore as it already has a lot of new components (e.g. -StrongPtr, Printf, and Scopeguard). Loki inspired similar tools andfunctionality now also present in the [1][2]Boost library collection. [1] http://en.wikipedia.org/wiki/Loki_(C%2B%2B) [2] http://loki-lib.sourceforge.net/

【设计模式】--C++设计模式类库 Loki介绍与用法

http://www.usidcbbs.com/simple/?t2428.html http://book.douban.com/subject/1119904/ C++设计模式类库 Loki介绍与用法 Loki是由Andrei编写的一个与《Modern C++ Design》(C++设计新思维)一书配套发行的C++代码库。 它不仅把C++模板的功能发挥到了极致,而且把类似设

_Witch__的专栏 4983

(c++设计新思维的库)loki

(c++设计新思维的库)loki (c++设计新思维的库)loki (c++设计新思维的库)loki

loki库v0.1.6

Loki是由Andrei编写的一个与《Modern C++ Design》(C++设计新思维)一书配套发行的C++代码库。 它不仅把C++模板的功能发挥到了极致,而且把类似设计模式这样思想层面的东西通过库来提供。

【亲测免费】 探索Loki库:高效C++编程的钥匙

探索Loki库:高效C++编程的钥匙 项目介绍 在C++的世界里,寻找一个能够优雅管理复杂设计模式和对象关系的工具是每个开发者梦寐以求的。Loki库,自2005年11月16日维护至今,正是这样一把钥匙。它不仅是一个集合了高级设计模式实现的开源库,更是提升C++程序健壮性和可维护性的得力助手。通过提供一系列精心设计的类模板,Loki使开发者能够轻松地在项目中集成如单例(Singleton)、小对象分...

gitblog_00032的博客 822

芯片低功耗设计实战:CPF与UPF流程选择指南(附工具对比)

本文深入探讨了芯片低功耗设计实战中CPF与UPF流程的选择策略。文章从两者本质差异与演进脉络出发,结合Cadence与Synopsys工具链的深度支持实况,为工程师提供了基于项目场景的决策框架。重点分析了在混合工具链环境下如何实施流程与验证,并强调了利用Spyglass等工具确保功耗意图无损传递的关键技巧,旨在帮助团队做出明智的流程抉择,提升设计效率与可靠性。

weixin_29197051的博客 407

递归之美 - Loki库TypeList源码剖析 (转)

递归之美 - Loki库TypeList源码剖析 (转)[@more@] 递归之美 - Loki库TypeList源码剖析 邓 辉   TypeList概观 提起List,想必大家都不会陌生,它是一个元...

csd3176的博客 184

银河麒麟V10系统下,手把手解决Ollama安装报错‘GLIBCXX_3.4.25 not found’

本文详细介绍了在银河麒麟V10系统下解决Ollama安装报错‘GLIBCXX_3.4.25 not found’的实战指南。通过隔离编译新版libstdc++、环境变量引导和服务化封装等安全方案,确保在不破坏系统稳定性的前提下顺利运行Ollama,适用于国产操作系统与AI工具的兼容性需求。

weixin_42556197的博客 484

自动驾驶车辆纵向控制实战:基于CARLA与ROS2的PID算法实现

本文详细介绍了基于CARLA与ROS2的自动驾驶车辆纵向控制实战,重点讲解了PID算法的实现与应用。通过搭建仿真环境、实现定速巡航和自适应巡航功能,帮助开发者掌握自动驾驶核心技术。文章还分享了系统集成、仿真验证及常见问题的调试技巧,为自动驾驶开发提供实用指导。

weixin_33728708的博客 355

LOKI库

递归之美 - Loki库TypeList源码剖析 转载于 http://blog.sina.com.cn/s/blog_45497dfa0100ppbp.htmlLOKI库读解:AbstractFactory 转载于 http://blog.sina.com.cn/s/blog_45497dfa0100ppbp.html

xcw_1987的博客 574

LTE随机接入实战:如何通过PRACH前导码优化小区覆盖半径(附计算公式)

本文深入探讨了LTE网络中PRACH前导码格式对小区覆盖半径的关键影响。通过解析不同前导码格式(如Format 0-3)的物理参数与覆盖能力,提供了核心计算公式与实战Python脚本,指导网络工程师根据覆盖需求、环境多径和容量等因素,科学选择前导码格式以优化边缘用户接入成功率,实现精准的网络规划与优化。

qq_27496129的博客 575

tars源码漫谈第1篇------tc_loki.h (牛逼哄哄的loki库)

loki库是C++模板大牛Andrei写的, 里面大量运用模板的特性, 而tc_loki.h借用了loki库的部分代码, 形成了一个基本的文件tc_loki.h, 来看看:#ifndef __TC_TYPETRAITS_H #define __TC_TYPETRAITS_H #include &lt;memory&gt; namespace tars { ///////////...

认知 行动 坚持 6620

SSE消息推送避坑指南:从Chrome兼容性到Spring超时配置

本文深入探讨了在Java Spring Boot项目中实现SSE消息推送时,从浏览器兼容性到后端配置的完整避坑指南。针对Chrome并发连接限制、Safari自动断开、Nginx代理缓冲以及Spring SseEmitter内存泄漏与超时管理等生产环境常见问题,提供了具体的解决方案和最佳实践配置,帮助开发者构建稳定可靠的实时推送系统。

work3的博客 450

Unity物理引擎实战:从刚体重心到碰撞体,打造真实不倒翁交互

刚体(Rigidbody)是Unity物理系统的核心组件,它定义了游戏对象的物理属性,如质量、阻力和碰撞响应。其原理基于牛顿力学,通过物理引擎(如PhysX)实时计算物体的运动状态。在游戏开发中,刚体的价值在于能够模拟真实世界的物理行为,为玩家提供沉浸式的交互体验。碰撞体(Collider)则定义了物体的形状边界,用于检测与其他物体的接触。通过精细调整刚体的重心(Center of Mass)和碰撞体形状,开发者可以模拟出各种有趣的物理现象,例如低重心物体的动态平衡。这在休闲游戏、物理玩具模拟等场景中尤为实

weixin_33976326的博客 355

《Modern C++ Design》Loki库源码读解随想 (转)

《Modern C++ Design》Loki库源码读解随想 (转)[@more@]《Modern C++ Design》Loki库源码读解随想 大牛Andrei Alexandrescu的《Modern C++ Design...

csd3176的博客 291

python机器人编程——用python实现一个写字机器人

本篇我们构建一个可以跟人一样写字的机器人python软件。实现如下功能:打开一个写字板,人类在屏幕上写或画出任意形状,机器人同步在纸面上画出和人类一样的形状,就好像人类在远程操控机械臂,又或是机械臂是人的另一只手。这个软件是可以扩展的,如果连上互联网,可以实现人在北京写了一行字,在上海的机器臂同时也在自己的本子上写了一行字。

JAMES费的空间 7994

loki使用超详细说明

前言:小伙伴们是用什么看日志的呢?我是用的loki,今天我跟大家分享下loki的使用,喜欢的话一键三连点收藏哦! loki使用超详细说明一、loki简介二、Loki 使用三、Loki语法说明四、Loki错误信息查看五、范围查询六、集合运算七、Loki Url 表达式编写八、Loki api使用 一、loki简介 Loki是 Grafana Labs 团队最新的开源项目,是一个水平可扩展,高可用性,多租户的日志聚合系统。它的设计非常经济高效且易于操作,因为它不会为日志内容编制索引,而是为每个日志流编制一组标签

李文良的博客 7万+

ACS运动控制器编程语言

ACS运动控制器采用SPiiPlusLanguage(SPL)作为核心编程语言,该语言基于C语法设计,内置丰富的运动控制指令,支持点位运动、插补等复杂控制。控制器提供专用开发工具SPiiPlusManager和ACSStudio,支持图形化编程与调试。同时兼容TCP/IP、EtherCAT等多种扩展接口,便于与上位机或PLC集成。使用时需注意实时性优先级、轴参数匹配等关键事项。官方文档和示例程序为开发者提供了完善的学习资源。

StevenChen的博客 624

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

4. 第四讲 loki::allocator

内存管理

梦心之魂的博客 777

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Loki库读解-为TypeList添足:最远基类、最近基类、类型的排序

相对于TypeList源码,下面代码还是很简单的,就不多说废话了。#ifndef TYPELIST_ADD_H#define TYPELIST_ADD_H//-------------------------------------------------------------------------------//Loki库自己的SUPERSUBCLASS_STRICT在DevCpp下

taodm的专栏 1497

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Loki之Funtion

Loki之Funtion http://www.cnblogs.com/UnGeek/p/4063571.html 阅读Loki中Funtion源码之后的个人理解,该库归纳起来可以说有三层(C++设计新思维列举到2个参数,此处列举到3个参数),要记住C++的模板其实就是C语言高级的宏定义,如果用户没有用到对应的模板编译器是不会生成对应代码的。 第一层:

zdy0_2004的专栏 596

《Modern C++ Design》Loki库读解三:多继承的改良

Loki库读解三:多继承的改良多继承,总是被我和goto联系在一起。从逻辑上,它们都是语言的必须组成部分,但在理论上被证明可以完全不使用它们。已经形式了这样的“圣条”:尽可能不要使用它们。并且甚至已被曲解为“绝对不要使用它们”。在Steve Maguire的《开发过程调试技术》中指出了可以安全使用goto的地方。而在Loki库中也指出了多继承的安全使用。几乎只有C++支持多重继承,不是

taodm的专栏 2540
上一篇: 软件测试认识中的误区
下一篇: Slashdot对Bjarne Stroustrup的采访
techcrunch
博客等级 码龄19年 34粉丝 3441原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值