V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
taowen
V2EX  ›  Python

C++ 17 写法上已经很接近 python 了

  •  4
     
  •   taowen · 2016-08-27 20:35:25 +08:00 · 37424 次点击
    这是一个创建于 3015 天前的主题,其中的信息可能已经有所发展或是发生改变。

    list 和 map

    本节例子选自: https://gist.github.com/JeffPaine/6213790

    对 python 这样的动态语言最直观的感受就是 list/map 两种数据结构打天下。 php 和 lua 甚至把这两个都合并成一种数据结构了。 毋庸置疑,学会如何使用 list 和 map 是基础中的基础。

    for 循环

    Python 版本

    import unittest
    
    class Test(unittest.TestCase):
        def test_foreach_on_lazy_range(self):
            for i in xrange(6):
                print i ** 2
    

    C++ 版本

    #include <catch_with_main.hpp>
    #include <range/v3/all.hpp>
    
    using namespace ranges;
    
    TEST_CASE("foreach on lazy range") {
        for(const auto& x : view::ints(0, 6)) {
            std::cout << x * x << std::endl;
        }
    }
    

    注意到 const auto& 的写法,这个表示我对这个变量进行只读的使用。只要是能用 const 的地方就用 const ( http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#a-namerconst-immutableacon1-by-default-make-objects-immutable )。 为什么还需要加上 reference ?因为非 reference 的版本默认的语义我要拥有这个变量( make a copy )。而在 for 循环下我们显然是只打算使用这个变量, 而不是去拥有一份。为什么不用指针而用引用?因为指针可空, reference 不可空。

    view::ints 是 range-v3 这个库提供的,作用等同于 xrange 。将来 range-v3 会成为标准库的一部分。

    foreach

    Python 版本

    import unittest
    
    class Test(unittest.TestCase):
        def test_foreach_on_list(self):
            colors = ['red', 'green', 'blue', 'yellow']
            for color in colors:
                print color
    

    C++ 版本

    #include <catch_with_main.hpp>
    
    using namespace ranges;
    
    TEST_CASE("foreach on list") {
        auto colors = {"red", "green", "blue", "yellow"};
        for(const auto& color : colors) {
            std::cout << color << std::endl;
        }
    }
    

    与 python 不同, c++没有所谓的默认的 list 类型。上面的写法是最简洁的写法。 colors 变量的实际类型 根据 GDB 是 std::initializer_list<const char*>。只有 begin , end , size 几个函数。实际上类似于 python 的 tuple 。 考虑到 python 的 list 类型是 mutable 的,所以更合适的实现是 std::vector 。

    #include <catch_with_main.hpp>
    
    using namespace ranges;
    
    TEST_CASE("foreach on vector") {
        auto colors = std::vector<const char*>{"red", "green", "blue", "yellow"};
        for(const auto& color : colors) {
            std::cout << color << std::endl;
        }
    }
    

    foreach 倒序

    Python 版本

    import unittest
    
    class Test(unittest.TestCase):
    
        def test_foreach_reversed(self):
            colors = ['red', 'green', 'blue', 'yellow']
            for color in reversed(colors):
                print(color)
    

    C++ 版本

    #include <catch_with_main.hpp>
    #include <range/v3/all.hpp>
    
    using namespace ranges;
    
    TEST_CASE("foreach reversed") {
        auto colors = std::vector<const char*>{"red", "green", "blue", "yellow"};
        for(const auto& color : colors | view::reverse) {
            std::cout << color << std::endl;
        }
    }
    

    这里使用了 range-v3 的 view 组合,类似 unix pipe 的语法。

    foreach 带下标

    Python 版本

    import unittest
    
    class Test(unittest.TestCase):
        def test_foreach_with_index(self):
            colors = ['red', 'green', 'blue', 'yellow']
            for i, color in enumerate(colors):
                print(i, color)
    

    C++ 版本

    #include <catch_with_main.hpp>
    #include <range/v3/all.hpp>
    
    using namespace ranges;
    
    TEST_CASE("foreach with index") {
        auto colors = std::vector<const char*>{"red", "green", "blue", "yellow"};
        for(const auto& [i, color] : view::zip(view::iota(0), colors)) {
            std::cout << i << " " << color << std::endl;
        }
    }
    

    view::iota这个的意思是产生一个从 n 开始的逐个加一的 view ,类似 python 里的 generator 。然后 zip 是把两个 view 逐个对应起来合并成一个 pair 的 view 。 然后const auto& [i, color]是 c++ 17 的 structured bindings 的写法,和 python 解开 tuple 里的元素的做法是如出一辙的。

    zip

    下面这个例子可以看得更清楚。 Python 版本

    import unittest
    import itertools
    
    class Test(unittest.TestCase):
        def test_zip(self):
            names = ['raymond', 'rachel', 'matthew']
            colors = ['red', 'green', 'blue', 'yellow']
            for name, color in itertools.izip(names, colors):
                print(name, color)
    

    izip 返回的是 generator 。 zip 返回都是 list 。 C++ 版本

    #include <catch_with_main.hpp>
    #include <range/v3/all.hpp>
    
    using namespace ranges;
    
    TEST_CASE("zip") {
        auto names = std::vector<const char*>{"raymond", "rachel", "matthew"};
        auto colors = std::vector<const char*>{"red", "green", "blue", "yellow"};
        for(const auto& [name, color] : view::zip(names, colors)) {
            std::cout << name << " " << color << std::endl;
        }
    }
    

    sorted

    Python 版本

    import unittest
    
    class Test(unittest.TestCase):
        def test_sort(self):
            colors = ['red', 'green', 'blue', 'yellow']
            for color in sorted(colors):
                print(color)
    

    C++ 版本

    #include <catch_with_main.hpp>
    #include <range/v3/all.hpp>
    
    using namespace ranges;
    
    TEST_CASE("sort") {
        auto colors = std::vector<std::string>{"red", "green", "blue", "yellow"};
        colors |= action::sort;
        for(const auto& color : colors) {
            std::cout << color << std::endl;
        }
    }
    

    这个例子里const char*换成了std::string,因为只有字符串类型才知道怎么比较,才能排序。 action::sort与 view 不同,它返回的是具体的 container ,而不再是 view 了。

    如果要倒过来排序,再 python 中是这样的

    import unittest
    
    class Test(unittest.TestCase):
        def test_sort_reverse(self):
            colors = ['red', 'green', 'blue', 'yellow']
            for color in sorted(colors, reverse=True):
                print(color)
    

    C++ 版本

    #include <catch_with_main.hpp>
    #include <range/v3/all.hpp>
    
    using namespace ranges;
    
    TEST_CASE("sort reverse") {
        auto colors = std::vector<std::string>{"red", "green", "blue", "yellow"};
        colors |= action::sort(std::greater<std::string>());
        for(const auto& color : colors) {
            std::cout << color << std::endl;
        }
    }
    

    Python 还支持指定属性去排序

    import unittest
    
    class Test(unittest.TestCase):
    
        def test_custom_sort(self):
            colors = ['red', 'green', 'blue', 'yellow']
            for color in sorted(colors, key=lambda e: len(e)):
                print(color)
    

    C++ 版本

    #include <catch_with_main.hpp>
    #include <range/v3/all.hpp>
    
    using namespace ranges;
    
    TEST_CASE("custom sort") {
        auto colors = std::vector<std::string>{"red", "green", "blue", "yellow"};
        colors |= action::sort(std::less<std::string>(), [](const auto&e) {
            return e.size();
        });
        for(const auto& color : colors) {
            std::cout << color << std::endl;
        }
    }
    

    sort的第一个参数是 comparator ,第二个参数是 projector 。这里我们使用了一个 lambda 表达式,从字符串上取得其长度值,用长度去排序。


    需要的编译环境

    • 操作系统: ubuntu 14.04
    • 编译器 clang 4.0: http://apt.llvm.org/ 使用 c++ 17
    • cmake: sudo apt-get install cmake
    • 包管理: git clone 依赖到自己的 repository ,所谓 vendoring
    • IDE clion : https://www.jetbrains.com/clion/
    • STL: libc++ 3.9
    • range-v3

    参见: https://taowen.gitbooks.io/modern-cpp-howto/content/unit-testing/chapter.html

    第 1 条附言  ·  2016-08-28 09:03:53 +08:00
    77 条回复    2016-08-31 12:18:19 +08:00
    kindjeff
        1
    kindjeff  
       2016-08-27 20:38:41 +08:00   ❤️ 11
    仿佛自己没有学过 C++…………
    taowen
        2
    taowen  
    OP
       2016-08-27 20:40:25 +08:00
    @kindjeff 2000 年的时候学的 c++,十六年之后重新再学一门新语言,只是名字还是叫 c++。有了 lambda , auto , structured bindings 之后, c++已经是一门适合制造语法糖的语言了。
    yangff
        3
    yangff  
       2016-08-27 20:40:34 +08:00
    第一个 Case 如果都是 int 的话…… 直接用 int 比较合算……
    hinkal
        4
    hinkal  
       2016-08-27 20:55:13 +08:00
    你们都在模仿 python 系列,《 java8 写法上已经很接近 python 了》
    int64ago
        5
    int64ago  
       2016-08-27 20:57:51 +08:00
    所以的你发的节点是 Python ...

    几年没看 C++ 发现已经不认识了
    qweweretrt515
        6
    qweweretrt515  
       2016-08-27 21:07:17 +08:00
    啊哈 放佛学过 c++
    alexapollo
        7
    alexapollo  
       2016-08-27 21:25:30 +08:00
    之前写过一个 C++ Python 化的库,语法比这个更简洁,但介入的点比较少。
    janxin
        8
    janxin  
       2016-08-27 21:26:09 +08:00
    我们来谈谈内存管理吧
    shijingshijing
        9
    shijingshijing  
       2016-08-27 21:29:49 +08:00
    强迫症表示只喜欢 C++这种括号反括号的代码方式~
    sc3263
        10
    sc3263  
       2016-08-27 21:33:39 +08:00
    @janxin 智能指针大法好~
    k9982874
        11
    k9982874  
       2016-08-27 21:35:37 +08:00 via iPad   ❤️ 1
    auto 这种语法糖真是蛋疼。不敢想象一个大型项目全是 auto 该多么欢乐。
    neosfung
        12
    neosfung  
       2016-08-27 21:36:54 +08:00
    我现在还只停留在 c++ 0x 上面。。。
    zhuangzhuang1988
        13
    zhuangzhuang1988  
       2016-08-27 21:36:57 +08:00
    我那个擦。。
    hitmanx
        14
    hitmanx  
       2016-08-27 21:48:13 +08:00
    有些还能忍忍,比如 range-based for loop,可能也是习惯 c++11 了。
    但是这种 pipeline style 看着实在太怪异了:
    1) for(const auto& color : colors | view::reverse)
    2) colors |= action::sort;

    本来很简单很纯粹的“或”,现在看来也得分情况去理解了,很讨厌这种二义性
    missdeer
        15
    missdeer  
       2016-08-27 21:50:33 +08:00
    大部分在 C++11 都实现了,那是 5 年前的东西了
    missdeer
        16
    missdeer  
       2016-08-27 21:53:17 +08:00
    @hitmanx 早年的各种 C++ practice 里都说过,不要乱用操作符重载,特别是重载出跟惯用含义不同的行为来,叹气
    misaka19000
        17
    misaka19000  
       2016-08-27 21:56:12 +08:00
    Python 这种算不算模仿 Lisp 呢?

    《黑客与画家》里面就说过,现代的各种编程语言都是在慢慢的模仿 Lisp 的写法,并且这种趋势还在慢慢的增强,一种语言模仿 Lisp 的部分越多就越强大
    regeditms
        18
    regeditms  
       2016-08-27 22:08:28 +08:00
    c++17 现在什么编译器才支持哈? vs2015 ?
    kingoldlucky
        19
    kingoldlucky  
       2016-08-27 22:08:57 +08:00
    算了 还是看看纯 C 就好了
    billlee
        20
    billlee  
       2016-08-27 22:10:16 +08:00
    int 类型还用常量引用不是蛋疼吗。。
    skydiver
        21
    skydiver  
       2016-08-27 22:10:36 +08:00 via iPad
    应该用右值引用吧 auto&&
    zhenyan
        22
    zhenyan  
       2016-08-27 22:11:59 +08:00
    @regeditms VS2020 还差不多
    jyf007
        23
    jyf007  
       2016-08-27 22:13:35 +08:00 via Android
    向着 lisp 和 smalltalk 前进
    taowen
        24
    taowen  
    OP
       2016-08-27 22:15:33 +08:00
    @regeditms clang++ 4.0 应该是目前唯一支持 structured bindings 的编译器
    wodesuck
        25
    wodesuck  
       2016-08-27 22:22:46 +08:00   ❤️ 1
    可以 这代码很骚
    不过主要骚的地方都是 range-v3 吧, c++17 的特性似乎就只有 auto [x, y] = xxxx 了
    htfy96
        26
    htfy96  
       2016-08-27 22:41:24 +08:00
    @billlee
    @yangff 开个 O2 gcc 下完全一样……
    ninjadq
        27
    ninjadq  
       2016-08-27 22:44:47 +08:00
    仿佛学过 C++ +1 = =
    tracymcladdy
        28
    tracymcladdy  
       2016-08-27 22:57:01 +08:00 via Android
    还是 c 纯粹
    regeditms
        29
    regeditms  
       2016-08-27 23:02:59 +08:00
    @taowen 哦, 看来 mac 上也要用 brew 安装 clang 最新版本了。
    bjrjk
        30
    bjrjk  
       2016-08-27 23:04:48 +08:00
    你确定这个是 C++?
    lzhCoooder
        31
    lzhCoooder  
       2016-08-27 23:32:47 +08:00
    带着手动管理内存的包袱,却有这么高级的语法真的合适吗?
    真的彻底和 ANSI C 分道扬镳了
    lsmgeb89
        32
    lsmgeb89  
       2016-08-27 23:41:09 +08:00
    现在除了 cppcon 哪里还能看点 C++17 的东西?
    yangff
        33
    yangff  
       2016-08-27 23:54:54 +08:00
    @k9982874 还好, C++的 auto 都能推倒类型
    taowen
        34
    taowen  
    OP
       2016-08-27 23:56:55 +08:00   ❤️ 1
    yuankui
        35
    yuankui  
       2016-08-28 09:21:49 +08:00   ❤️ 1
    这语法,丑的一笔。。
    svenFeng
        36
    svenFeng  
       2016-08-28 09:34:58 +08:00 via Android   ❤️ 1
    感觉适应一下新标准花不了多少时间,还可以学到很多东西,可现在也很多人还是用 C+class 操着 C++,很多人声称 STL 就是个垃圾(ーー;)
    sinopec
        37
    sinopec  
       2016-08-28 09:50:05 +08:00
    c++坑太深,奇技淫巧跟新东西太多,学无止境啊,而且学的大部分还都用不到,好处是,现在学啥语言都不太费力....
    linux40
        38
    linux40  
       2016-08-28 09:55:27 +08:00 via Android
    c++17 标准库还是加了很多东西嘛。。。
    htfy96
        39
    htfy96  
       2016-08-28 09:56:28 +08:00 via Android   ❤️ 1
    感觉国内的风向就是
    1 纯 C 好 简洁
    2 学了 C 自然就懂 C++了
    3 新标准有什么用,一堆公司还在用 C++98
    4 C++=C + STL 或者退化成 C with class
    5 C++异常不管什么时候都差劲 还是 errCode 靠谱

    虽然是因为国内一些客观因素决定的(比如说教学、招人成本之类的),但是这样下去只能会搞成国内特色的东西
    linux40
        40
    linux40  
       2016-08-28 09:58:31 +08:00 via Android
    至少动态语言是在模仿 lisp 。。。
    gimp
        41
    gimp  
       2016-08-28 10:00:07 +08:00
    看 ES6 ,我还以为 python 开发者打入他们的标准委员会内部了呢
    mind3x
        42
    mind3x  
       2016-08-28 11:20:48 +08:00 via Android
    @hitmanx 这个地方根据上下文看其实还好
    bobylive
        43
    bobylive  
       2016-08-28 11:57:06 +08:00 via Android
    C 艹的坑这是一年比一年深了
    relcodego
        44
    relcodego  
       2016-08-28 12:57:20 +08:00 via Android
    已经回不去 c++了。。。
    kingddc314
        45
    kingddc314  
       2016-08-28 14:25:36 +08:00
    这几乎都是 C++11 的东西, C 风格写 C++的就不要大惊小怪了
    bombless
        46
    bombless  
       2016-08-28 15:37:24 +08:00 via Android
    cpp 比较尴尬的还是 split ……基本只能到处拷贝
    palxex
        47
    palxex  
       2016-08-28 16:01:21 +08:00
    这只是看起来像而已。用 C++写一个字面量 json 试试?不用复杂,{"int":1,"string":"test"}就行。
    用 python 写东西时感受的随心所欲,很大一个方面来自于它的动态类型不受 expression problem 影响,这是它受 lisp 影响最核心的部分,而不是流于表面的 map/reduce/filter 支持。这个问题是 C++过去,现在以及看得到的将来都不可能解决的。
    taowen
        48
    taowen  
    OP
       2016-08-28 16:25:59 +08:00
    @palxex
    ```
    #include <catch_with_main.hpp>
    #include <json.hpp>

    using json = nlohmann::json;

    TEST_CASE("json literal") {
    json obj {
    {"int", 1},
    {"string", "test"},
    };
    std::cout << obj << std::endl;
    }
    ```

    https://github.com/nlohmann/json
    palxex
        49
    palxex  
       2016-08-28 16:44:09 +08:00
    @taowen 还真有啊!多谢,我看看这个库。以前此类库最大的问题是极难与 STL 协作,因为 json 的本质要求此类库必须自己搞出自己的单根体系当 any 。
    secondwtq
        50
    secondwtq  
       2016-08-28 18:39:40 +08:00
    涨姿势了

    不过我还是喜欢“ C++ 特色”,像不像 Python 对我来说并没有什么所谓
    soland
        51
    soland  
       2016-08-28 19:20:26 +08:00   ❤️ 1
    语法糖衣,里面还是炮弹。
    SuperFashi
        52
    SuperFashi  
       2016-08-28 19:43:02 +08:00
    真的就是语法糖而已。让我比较惊奇的是 C++17 里自带 gcd 和 lcm ,不过这是基本功。
    不管怎么样,目前 NOI/P 只能用 C++98 ,所以这些对我来说没什么用。
    什么,你说不打竞赛的时候用?不大竞赛用个 P 的 C++,放着大好的 Python 和 Go 不用。
    SuperFashi
        53
    SuperFashi  
       2016-08-28 19:49:40 +08:00
    不过吐槽一句, python 实例代码不是很 pythonic ,例如排序那个写成 sorted(colors, key=len) 就好。
    xuboying
        54
    xuboying  
       2016-08-28 20:00:37 +08:00 via Android
    taowen
        55
    taowen  
    OP
       2016-08-28 20:41:40 +08:00
    @SuperFashi 故意写成 lambda 让对比更明显的。这不是比赛,只是让 python 程序员快速找到 c++里的对等概念。
    htfy96
        56
    htfy96  
       2016-08-28 21:52:23 +08:00
    @SuperFashi Golang 还是算了吧。。天天写的要吐了……。语法太简洁导致要用自定义功能的时候会很繁琐……来试试写一个自定义 key 的 sort 就知道了……
    mozartgho
        57
    mozartgho  
       2016-08-28 22:55:25 +08:00
    C++的写法比起 Python 仍然不够简洁,感觉委员会的那帮人把 C++搞得越来越复杂了,各种新的语法糖。编译器严重落后标准。
    wshcdr
        58
    wshcdr  
       2016-08-28 23:04:13 +08:00
    C++努力地向动态语言靠近,不过,个人不看好
    taowen
        59
    taowen  
    OP
       2016-08-28 23:46:41 +08:00
    @mozartgho http://en.cppreference.com/w/cpp/compiler_support 目测还好。编译器严重落后标准这个说法是不是来自于 template export 这个特性?
    srlp
        60
    srlp  
       2016-08-29 01:47:06 +08:00 via iPhone   ❤️ 1
    @palxex json 也可以用 folly::dynamic ,会尽量 cast 到原生类型。
    jeffersonpig
        61
    jeffersonpig  
       2016-08-29 08:46:57 +08:00
    好吧原来我一直是个 C++门外汉
    gougudingli
        62
    gougudingli  
       2016-08-29 08:57:12 +08:00
    有些编程书,还在让读者装 VC++6.o 。心生疑惑: C++11 、 17 那么多版本,究竟是哪些人来维护和修改的?
    stormpeach
        63
    stormpeach  
       2016-08-29 09:03:57 +08:00
    @hitmanx 不能更同意,所以现在去学 rust 了。。。
    stormpeach
        64
    stormpeach  
       2016-08-29 09:08:08 +08:00
    看来又要出一本新的书了:《 effective range-v3 》。。。
    lcc4376
        65
    lcc4376  
       2016-08-29 09:11:49 +08:00
    上次寫 c++是 08 年的事
    gotham
        66
    gotham  
       2016-08-29 09:16:55 +08:00
    你是 sb 吗, TEST_CASE 这种写法出现十多年了。
    joye1230
        67
    joye1230  
       2016-08-29 09:31:25 +08:00
    re 从零开始 c++
    likebeta
        68
    likebeta  
       2016-08-29 09:37:48 +08:00
    @gotham 楼主再说 TEST_CASE ? 智商是硬伤
    hei1000
        69
    hei1000  
       2016-08-29 09:43:30 +08:00
    搞得这么复杂,还是喜欢 C,尽管有缺陷,但也没看像 C++这样什么都往里面家
    wubotao
        70
    wubotao  
       2016-08-29 09:58:51 +08:00
    好久不写 C++ 了。
    bp0
        71
    bp0  
       2016-08-29 10:14:38 +08:00
    算了,我已经不认识 C++了,还是默默的写 C 吧。
    cppgohan
        72
    cppgohan  
       2016-08-29 10:25:02 +08:00
    不说语言之争, 语言工具各有利弊. :)

    view 是默认的命名空间吗? 略微有点奇怪.

    好久没在生产环境写 C++, 从没在生产环境写过 C++11, 不知道这里哪些地方用了 C++17 的哪些特性? 楼主可以多分享分享, 还是挺有兴趣的.


    至于 python, 主要是写起来简单, 不用考虑如同 c++那种更复杂完备的 lambda 语法, 常量引用这些.

    比如你的例子中的排序, python 一行列表析取式就直接搞定了.
    print [x for x in sorted(['red', 'green', 'blue', 'yellow'])]


    这两年工作以做 android 开发 /java 语言为主, 过去的 c++底子有点秀逗了. python 也是更早自学敲敲打打的.

    但是 python 捡起来相比 c++还是容易太多了.

    C++对人的要求更高些, 暴露的更多细节, 如果经验攒的不够, 更容易写出更坏味道的代码.

    就如同 steve yegge 说 java 语言: Java 语言有个优点, 它让那些烂程序员造不成太大的破坏性:).
    taowen
        73
    taowen  
    OP
       2016-08-29 10:31:11 +08:00
    @cppgohan 所以需要更多 http://clang.llvm.org/extra/clang-tidy/ 和 clion 这样的 IDE ,把经验变成静态检查。新的 GSL 规范利用静态检查来做所有权和边界安全。基本上相当于是利用静态检查在 c++的基础上发明一个子集的语言的感觉了。这门新的语言虽然还叫 c++,但是就不那么需要依赖程序员自律来保障安全了。
    wizardforcel
        74
    wizardforcel  
       2016-08-29 10:46:31 +08:00
    我之前把一个 node 写的代码用 c++11 翻写了一遍,发现除了不支持字典的字面值,其它还好。
    wangkun2012324
        75
    wangkun2012324  
       2016-08-29 16:31:02 +08:00
    这样看来, Swift 还是不错的,哈哈哈
    andypinet
        76
    andypinet  
       2016-08-30 13:04:36 +08:00
    c++ 23 估计可以更 python
    lellansin
        77
    lellansin  
       2016-08-31 12:18:19 +08:00
    可啪
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1408 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 23:46 · PVG 07:46 · LAX 15:46 · JFK 18:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.