• structc 开源框架介绍


    引言 - 一切才刚刚开始

      structc 是 C 结构基础库. 简单可复用. 

      structc - https://github.com/wangzhione/structc

      之前也描述过几次 structc, 文字多代码风格少. 最近加班不多, 准备详细解说哈其思考初衷.

    0.0 整体结构

    structc
    ├── extern
    ├── LICENSE
    ├── Makefile
    ├── README.md
    ├── structc
    └── structc.sln

    structc.sln       : winds  项目管理文件 visual studio 

    structc           : 项目整体源码和素材文件目录

    README.md  : 项目介绍 Markdown

    Makefile          : linux 编译文件 make 

    LICENSE        : MIT 开源协议

    extern             : 项目引入的外部库目录

    extern
    ├── jemalloc
    ├── jemalloc-vc141-Release-static.lib
    ├── libuv.lib
    ├── pthread.h
    ├── pthread_lib.lib
    ├── sched.h
    ├── semaphore.h
    ├── strings.h
    ├── uv
    └── uv.h

    以上就是我们看到 structc 项目整体结构.

    0.1 外部库

      当前很谨慎的引入两个半外部库.  最大程度会静态库编译链接运行. 荣我慢慢细说.

    1. jemalloc - https://github.com/jemalloc/jemalloc

      jemalloc 是 c 构建底层高性能 malloc 库. 也被称为系统编程末期最后免费午餐. 整个 structc

    malloc 全权交给 je_malloc 抗压.  其中 winds 编译静态库部分, 项目本身也有细说 - 

    https://github.com/jemalloc/jemalloc/tree/dev/msvc

    How to build jemalloc for Windows
    =================================
    
    1. Install Cygwin with at least the following packages:
       * autoconf
       * autogen
       * gawk
       * grep
       * sed
    
    2. Install Visual Studio 2015 or 2017 with Visual C++
    
    3. Add Cygwinin to the PATH environment variable
    
    4. Open "x64 Native Tools Command Prompt for VS 2017"
       (note: x86/x64 doesn't matter at this point)
    
    5. Generate header files:
       sh -c "CC=cl ./autogen.sh"
    
    6. Now the project can be opened and built in Visual Studio:
       msvcjemalloc_vc2017.sln

    ( 注: vs 使用最新版本. 网址打不开那就翻墙. 后面其也一样, 时刻保证最新 2018/10/10 ~ )   

    对于 linux 编译安装参照下面脚本 

    # 开发环境安装
    sudo apt install gcc gdb autogen autoconf
    
    # jemalloc 安装
    cd
    wget https://github.com/jemalloc/jemalloc/releases/download/5.1.0/jemalloc-5.1.0.tar.bz2
    tar -jxvf jemalloc-5.1.0.tar.bz2
    cd jemalloc-5.1.0
    
    sh autogen.sh
    make -j4
    sudo make install
    sudo ldconfig
    
    cd
    rm -rf jemalloc-5.1.0 jemalloc-5.1.0.tar.bz2

    当 jemalloc 构建好了. 设计 alloc 层引入到 structc 框架中, 用户取代系统 malloc...

    alloc.h - https://github.com/wangzhione/structc/blob/master/structc/system/alloc.h

    #ifndef _H_ALLOC
    #define _H_ALLOC
    
    #include <stdlib.h>
    #include <string.h>
    
    // :) 高效内存分配, 莫名伤感 ~
    // _MSC_VER -> Winds CL
    // __GNUC__ -> Linux GCC
    //
    #ifdef _MSC_VER
    //
    // CPU 检测 x64 or x86
    // ISX64 defined 表示 x64 否则 x86
    //
    #   if defined(_M_ARM64) || defined(_M_X64)
    #       define ISX64
    #   endif
    //
    // _M_PPC 为 PowerPC 平台定义, 现在已不支持
    // so winds 可以认为都是小端平台
    //
    #   if defined(_M_PPC)
    #       define ISBENIAN
    #   endif
    
    #elif  __GNUC__
    
    #   if defined(__x86_64__)
    #       define ISX64
    #   endif
    //
    // 大小端检测 : ISBENIAN defined 表示大端
    //
    #   if defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN_BITFIELD)
    #       define ISBENIAN
    #   endif
    
    #else
    #   error BUILD ( ̄︶ ̄) S
    #endif
    
    // OFF_ALLOC - 关闭全局 free / malloc 配置
    #ifndef OFF_ALLOC
    
    #   undef  free
    #   define free    free_
    
    #   undef  strdup
    #   define strdup  strdup_
    
    #   undef  malloc
    #   define malloc  malloc_
    #   undef  calloc
    #   define calloc  calloc_
    #   undef  realloc
    #   define realloc realloc_
    
    #endif//OFF_ALLOC
    
    //
    // free_ - free 包装函数
    // ptr      : 内存首地址
    // return   : void
    //
    extern void free_(void * ptr);
    
    //
    // malloc_ - malloc 包装, 封装一些特殊业务
    // size     : 分配的内存字节
    // return   : 返回可使用的内存地址.
    //
    extern void * malloc_(size_t size);
    
    //
    // strdup_ - strdup 包装函数
    // s        : '' 结尾 C 字符串
    // return   : 拷贝后新的 C 字符串
    //
    extern char * strdup_(const char * s);
    
    //
    // calloc_ - calloc 包装, 封装一些特殊业务
    // num      : 数量
    // size     : 大小
    // return   : 返回可用内存地址, 并且置0
    //
    extern void * calloc_(size_t num, size_t size);
    
    //
    // realloc_ - realoc 包装函数, 封装一些特殊业务
    // ptr      : 内存首地址, NULL 等同于 malloc
    // size     : 重新分配的内存大小
    // return   : 返回重新分配好的新地址内容
    //
    extern void * realloc_(void * ptr, size_t size);
    
    #endif//_H_STDEXIT

    alloc.c - https://github.com/wangzhione/structc/blob/master/structc/system/alloc.c

    #include <stdio.h>
    
    #define OFF_ALLOC
    #include "alloc.h"
    
    #define JEMALLOC_NO_DEMANGLE
    #include <jemalloc/jemalloc.h>
    
    //
    // free_ - free 包装函数
    // ptr      : 内存首地址
    // return   : void
    //
    inline void free_(void * ptr) {
        je_free(ptr);
    }
    
    // 简单内存不足检测处理
    static inline void * mcheck(void * ptr, size_t size) {
        if (NULL == ptr) {
            fprintf(stderr, "out of memory trying to allocate %zu
    ", size);
            fflush(stderr);
            abort();
        }
        return ptr;
    }
    
    //
    // malloc_ - malloc 包装, 封装一些特殊业务
    // size     : 分配的内存字节
    // return   : 返回可使用的内存地址.
    //
    inline void * malloc_(size_t size) {
        void * ptr = je_malloc(size);
        return mcheck(ptr, size);
    }
    
    //
    // strdup_ - strdup 包装函数
    // s        : '' 结尾 C 字符串
    // return   : 拷贝后新的 C 字符串
    //
    inline char * strdup_(const char * s) {
        if (s) {
            size_t n = strlen(s) + 1;
            char * ptr = malloc_(n);
            return memcpy(ptr, s, n);
        }
        return NULL;
    }
    
    //
    // calloc_ - calloc 包装, 封装一些特殊业务
    // num      : 数量
    // size     : 大小
    // return   : 返回可用内存地址, 并且置0
    //
    inline void * calloc_(size_t num, size_t size) {
        void * ptr = je_calloc(num, size);
        return mcheck(ptr, size);
    }
    
    //
    // realloc_ - realoc 包装函数, 封装一些特殊业务
    // ptr      : 内存首地址, NULL 等同于 malloc
    // size     : 重新分配的内存大小
    // return   : 返回重新分配好的新地址内容
    //
    inline void * realloc_(void * ptr, size_t size) {
        void * ntr = je_realloc(ptr, size);
        return mcheck(ntr, size);
    }

    包装了一层. 从 alloc.h 中 OFF_ALLOC 宏可以看出, 具备支持插拔能力 ~

    2. libuv - https://github.com/libuv/libuv  

      libuv 用 c 写的高性能单线程网络 io 库. 希望通过它来支撑网络层.  winds 编译静态库

    参照 libuv 项目首页燥起来就行. 其中 gyp 安装了这个版本, 其它随波逐流 ~

      gyp - https://github.com/adblockplus/gyp

    linux 编译安装脚本 

    # libuv 安装
    cd
    wget https://github.com/libuv/libuv/archive/v1.23.1.zip
    unzip v1.23.1.zip
    cd libuv-1.23.1
    
    sh autogen.sh
    ./configure
    make -j4
    sudo make install
    sudo ldconfig
    
    cd
    #
    # 注意 uv 头文件, 全部导入到系统 include 目录下面
    #
    rm -rf libuv-1.23.1 v1.23.1.zip

    注意要将编译后 include 完整拷贝到安装目录 include下. 这样 uv 头文件全, 日后会用到.  

    libuv 开箱即用, 不太需要什么基础封装. 

    3. pthread - https://github.com/GerHobbelt/pthread-win32

      这是最后那半个, 为 winds 引入 POSIX thread 模型.  编译起来很简单(前提咱们 VS 玩的熟). 

    扯点闲篇. linux 和 winds 相辅相成, 对立而统一. 一个是一切从头码, 一个开始就已经注册未来.

    描述比较粗, 但大概这意思.  (两个都不 eary, 玩很久才敢入门见岳父岳母) . 这里包装了一层

    thread.h - https://github.com/wangzhione/structc/blob/master/structc/system/thread.h

    #ifndef _H_THREAD
    #define _H_THREAD
    
    #include "struct.h"
    #include <pthread.h>
    #include <semaphore.h>
    
    //
    // pthread_end - 等待启动线程结束
    // tid      : 线程id
    // return   : void
    //
    inline void pthread_end(pthread_t tid) {
        pthread_join(tid, NULL);
    }
    
    //
    // pthread_run - 异步启动线程
    // id       : &tid 线程id地址
    // frun     : 运行的主体
    // arg      : 运行参数
    // return   : 返回线程构建结果, 0 is success
    //
    #define pthread_run(id, frun, arg)                                  
    pthread_run_(&(id), (node_f)(frun), (void *)(intptr_t)(arg))
    inline int pthread_run_(pthread_t * id, node_f frun, void * arg) {
        return pthread_create(id, NULL, (start_f)frun, arg);
    }
    
    //
    // pthread_async - 异步启动分离线程
    // frun     : 运行的主体
    // arg      : 运行参数
    // return   : 返回 0 is success
    // 
    #define pthread_async(frun, arg)                                    
    pthread_async_((node_f)(frun), (void *)(intptr_t)(arg))
    inline int pthread_async_(node_f frun, void * arg) {
        int ret;
        pthread_t tid;
        pthread_attr_t attr;
    
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        ret = pthread_create(&tid, &attr, (start_f)frun, arg);
        pthread_attr_destroy(&attr);
    
        return ret;
    }
    
    #endif//_H_THREAD

    利用现代编译器兼容性构建了 pthread 两种启动宏, 后续写 pthread create 相关代码会得心应手!  

    到此我们大一统治线程模型就定下来了. 还顺带引出了一个很重要辅助头文件. 

    struct.h - https://github.com/wangzhione/structc/blob/master/structc/struct/struct.h

    #ifndef _H_STRUCT
    #define _H_STRUCT
    
    #include <math.h>
    #include "alloc.h"
    #include <ctype.h>
    #include <float.h>
    #include <stdio.h>
    #include <errno.h>
    #include <assert.h>
    #include <stdarg.h>
    #include <stdint.h>
    #include <stddef.h>
    #include <limits.h>
    #include <stdbool.h>
    #include <inttypes.h>
    
    //
    // enum Flag int - 函数返回值全局状态码
    // >= 0 标识 Success 状态, < 0 标识 Error 状态
    //
    enum {
        SBase       =  +0, // 正确基础类型
    
        EBase       =  -1, // 错误基础类型
        EParam      =  -2, // 输入参数错误
        EFd         =  -3, // 文件打开失败
        EClose      =  -4, // 文件操作关闭
        EAccess     =  -5, // 没有操作权限
        EAlloc      =  -6, // 内存操作错误
        EParse      =  -7, // 协议解析错误
        ESmall      =  -8, // 过小基础错误
        EBig        =  -9, // 过大基础错误
        ETimeout    = -10, // 操作超时错误
    };
    
    //
    // DCODE - DEBUG 模式下的测试宏
    // DCODE({
    //     puts("debug start...");
    // });
    //
    #ifndef DCODE
    #   ifdef _DEBUG
    #       define DCODE(code)  do code while(0)
    #   else
    #       define DCODE(code)  
    #   endif //  ! _DEBUG
    #endif  //  ! DCODE
    
    //
    // icmp_f - 比较行为的类型
    //  : int add_cmp(const void * now, const void * node)
    //
    typedef int (* icmp_f)();
    
    //
    // vnew_f - 根据规则构建对象
    //  : void * rtree_new(void * node)
    //
    typedef void * (* vnew_f)();
    
    //
    // node_f - 销毁当前对象节点
    //  : void list_die(void * node); 
    //
    typedef void (* node_f)(void * node);
    
    //
    // start_f - pthread create func
    //  : int * run(int * arg)
    //
    typedef void * (* start_f)(void * arg);
    
    //
    // each_f - each 循环操作, arg 外部参数, node 是内部结点
    //  : int dict_echo(struct dict * node, void * arg) { return 0; }
    //
    typedef int (* each_f)(void * node, void * arg);
    
    //
    // CERR - 打印错误信息
    // EXIT - 打印错误信息, 并 exit
    // IF   - 条件判断异常退出的辅助宏
    //
    #define CERR(fmt, ...)                                                   
    fprintf(stderr, "[%s:%s:%d][%d:%s]" fmt "
    ",                            
        __FILE__, __func__, __LINE__, errno, strerror(errno), ##__VA_ARGS__)
    
    #define EXIT(fmt, ...)                                                   
    do {                                                                     
        CERR(fmt, ##__VA_ARGS__);                                            
        exit(EXIT_FAILURE);                                                  
    } while(0)
    
    #define IF(cond)                                                         
    if ((cond)) EXIT(#cond)
    
    //
    // RETURN - 打印错误信息, 并 return 返回指定结果
    // val      : return的东西, 当需要 return void; 时候填 ',' 就过 or NIL
    // fmt      : 双引号包裹的格式化字符串
    // ...      : fmt中对应的参数
    // return   : val
    // 
    #define RETURN(val, fmt, ...)                                           
    do {                                                                    
        CERR(fmt, ##__VA_ARGS__);                                           
        return val;                                                         
    } while(0)
    
    #define NIL
    #define RETNIL(fmt, ...)                                                
    RETURN(NIL , fmt, ##__VA_ARGS__)
    
    #define RETNUL(fmt, ...)                                                
    RETURN(NULL, fmt, ##__VA_ARGS__)
    
    #endif//_H_STRUCT

    作者尝试写 structc 项目时第一个源文件 : )

    0.2 IDE 弱议

       winds 没得选, 最新最全的 visual studio best version 有才能统治一切. 这里主要说

    的是 linux 上面我们的选择. 最开始我是 vi + make + gcc + gdb 开发和编译的. 

    Makefile - https://github.com/wangzhione/structc/blob/master/Makefile 

    # 编译的目录结构
    # Release : make 
    # Debug   : make D=-D_DEBUG
    # Clean   : make clean

    make 是编译发布, make D=-D_DEBUG 是编译 Debug, make clean 项目清理. 手工操作. 

    这样搞对我都还好, 什么都行. 

    但不妨更精进一步 [vi + make + gcc + gdb] -> [code + F5 + F10 + F11] 是不是更妙.

    微软作为桌面软件霸主, code(VSCode 简称)不用我多说, 不得不服. 那开搞

    1. 安装软件

      ubuntu best version 

      vscode 

     安装好 vscode 后, 在其内部安装插件 Microsoft C/C++ for Visual Studio Code

    2. F1 -> Edit Configurations -> c_cpp_properties.json

     设置如下内容和VS配置很相似

    {
        "configurations": [
            {
                "name": "Linux",
                "includePath": [
                    "/usr/include/c++/7",
                    "/usr/include/x86_64-linux-gnu/c++/7",
                    "/usr/include/c++/7/backward",
                    "/usr/lib/gcc/x86_64-linux-gnu/7/include",
                    "/usr/local/include",
                    "/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed",
                    "/usr/include/x86_64-linux-gnu",
                    "/usr/include",
                    "${workspaceRoot}",
                    "${workspaceRoot}/structc/base",
                    "${workspaceRoot}/structc/struct",
                    "${workspaceRoot}/structc/system"
                ],
                "defines": [
                    "_DEBUG",
                    "__GNUC__"
                ],
                "intelliSenseMode": "clang-x64",
                "browse": {
                    "path": [
                        "/usr/include/c++/7",
                        "/usr/include/x86_64-linux-gnu/c++/7",
                        "/usr/include/c++/7/backward",
                        "/usr/lib/gcc/x86_64-linux-gnu/7/include",
                        "/usr/local/include",
                        "/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed",
                        "/usr/include/x86_64-linux-gnu",
                        "/usr/include",
                        "${workspaceRoot}"
                    ],
                    "limitSymbolsToIncludedHeaders": true,
                    "databaseFilename": ""
                },
                "compilerPath": "/usr/bin/clang",
                "cStandard": "c11",
                "cppStandard": "c++17"
            }
        ],
        "version": 4
    }

    3. F5 -> launch.json

        按照规律改 program 项目生成 和 preLaunchTask 前置任务
    {
        // 使用 IntelliSense 了解相关属性。 
        // 悬停以查看现有属性的描述。
        // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
        "version": "0.2.0",
        "configurations": [
            {
                "name": "(gdb) Launch",
                "type": "cppdbg",
                "request": "launch",
                "program": "${workspaceFolder}/Out/main.exe",
                "args": [],
                "stopAtEntry": false,
                "cwd": "${workspaceFolder}",
                "environment": [],
                "externalConsole": true,
                "preLaunchTask": "Debug",
                "MIMode": "gdb",
                "setupCommands": [
                    {
                        "description": "Enable pretty-printing for gdb",
                        "text": "-enable-pretty-printing",
                        "ignoreFailures": true
                    }
                ]
            }
        ]
    }

    4. F5 -> tasks.json

    建立下面任务, 目前只使用了 Debug
    {
        // See https://go.microsoft.com/fwlink/?LinkId=733558
        // for the documentation about the tasks.json format
        "version": "2.0.0",
        "tasks": [
            {
                "type"    : "shell",
                "label"   : "Debug",
                "command" : "make D=-D_DEBUG"
            }
        ]
    }

    此刻我们就可以 F5 搞起来 ~

    兄弟们是不是很亲切, 这么复杂定制化项目都可以可视化调试. 还有谁 ~ 当然 IDE 有没有

    都好说, 难说的是你是否耐的下心去感悟技术的脉络, 可不能学京东技术, 对开源缺失敬畏

    之心, 技术不见得多厉害, 节操提前贷款没了 ~ 最终成为奥义之梗 : )

    前言 -  不妨说点设计

      进入 structc/structc 看到以下项目结构

    wzhi@wzc:~/structc/structc$ tree -L 1
    .
    ├── base
    ├── conf
    ├── main
    ├── README.md
    ├── struct
    ├── structc.vcxproj
    ├── structc.vcxproj.filters
    ├── structc.vcxproj.user
    ├── system
    └── test

    base      : 基础接口封装目录

    conf       : 配置文件目录

    main      : 主函数目录

    struct    : 数据结构接口目录

    system  : 系统库包装目录

    test        : 单元测试目录

    1.0 main 主函数设计

    wzhi@wzc:~/structc/structc/main$ tree
    .
    ├── main.c
    ├── main_init.c
    ├── main_run.c
    └── main_test.c

    重点关注下入口 mian 主函数设计 main.c 

    #include "head.h"
    
    //
    // main - 程序的总入口, 从扯开始
    // argc     : 输入参数个数
    // argv     : 参数集
    // return   : 返回程序退出的状态码
    //
    int main(int argc, char * argv[]) {
        //
        // 初始化 ... ...
        // ... ...
        EXTERN_RUN(main_init);
    
        //
        // make D=-D_DEBUG
        // main_test 单元测试才会启动
        //
    #ifdef _DEBUG
        EXTERN_RUN(main_test);
    #endif
    
        // ... 
        // ... 启动当前项目运行的主函数
        //
        EXTERN_RUN(main_run, argc, argv);
    
        return EXIT_SUCCESS;
    }

    其中 EXTERN_RUN 也很奇巧

    //
    // EXTERN_RUN - 简单的声明, 并立即使用的宏
    // ftest    : 需要执行的函数名称
    // ...      : 可变参数, 保留
    //
    #define EXTERN_RUN(ftest, ...)                                  
    do {                                                            
        extern void ftest();                                        
        ftest (__VA_ARGS__);                                        
    } while(0)

    越过声明直接使用的宏声明. structc 中 main 函数一共做了二件半事情.

    main_init 初始化函数, main_run 业务运行函数, 还有半个 main_test 运行单元测试.

    随后我们好好看看这个单元测试套路. 

    1.1 test 单元测试套路设计

    先看看 main_test.c 

    #include "head.h"
    
    //
    // TEST - 用于单元测试函数, 执行并输出运行时间
    // ftest    : 需要执行的测试函数名称
    // ...      : 可变参数, 保留
    //
    #define TEST(ftest, ...)                                         
    do {                                                             
        extern void ftest();                                         
        clock_t $s = clock();                                        
        ftest (##__VA_ARGS__);                                       
        double $e = (double)clock();                                 
        printf(STR(ftest)" run time:%lfs
    ", ($e-$s)/CLOCKS_PER_SEC);
    } while(0)
    
    
    //
    // main_test - *_test is here run
    // return   : void
    //
    void main_test(void) {
        //
        // 开始你的表演, 单元测试
        //
    
        EXTERN_RUN(uv_tty_test);
    }

    以上只给予了业务测试的能力. 其中 uv_tty_test 函数就是单元测试目录下其中一个的单元测试函数体.

    而我们每个业务测试函数, 顺带会创建一个同名的 .c 文件. 例如这里是 uv_tty_test.c 

    #include <uv.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    //
    // 测试 libuv tty 操作控制台
    // 输出一段有颜色的文字
    //
    void uv_tty_test(void) {
        uv_tty_t tty;
        uv_buf_t buf[3];
        unsigned i, len = sizeof buf / sizeof *buf;
        uv_loop_t * loop = uv_default_loop();
    
        // 目前只对 tty 控制台处理
        if (uv_guess_handle(1) != UV_TTY) {
            fprintf(stderr, "uv_guess_handle(1) != UV_TTY!
    ");
            exit(EXIT_FAILURE);
        }
    
        uv_tty_init(loop, &tty, 1, 0);
        uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL);
    
        // 开始发送消息
        buf[0].base = "33[46;37m";
        buf[1].base = u8"(✿◡‿◡) 喵酱 ((●'-'●)) 比 ♥ 里~ 
    ";
        buf[2].base = "33[0m";
        for (i = 0; i < len; ++i)
            buf[i].len = (int)strlen(buf[i].base);
        uv_try_write((uv_stream_t *)&tty, buf, len);
    
        // 重置终端行为
        uv_tty_reset_mode();
        uv_run(loop, UV_RUN_DEFAULT);
    }

    思路很直白. 这些就是单元测试的真相... . 比较清晰的展示(业务是复杂中减负) 

    1.2 system 系统库设计

    这里面设计东东不少, 只挑一些经典的供人看看. 代码即注释 ~

    socket.h - https://github.com/wangzhione/structc/blob/master/structc/system/socket.h

    #ifndef _H_SOCKET
    #define _H_SOCKET
    
    #include <time.h>
    #include <fcntl.h>
    #include "struct.h"
    #include <signal.h>
    #include <sys/types.h>
    
    #ifdef __GNUC__
    
    #include <netdb.h>
    #include <unistd.h>
    #include <sys/un.h>
    #include <sys/uio.h>
    #include <sys/time.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <netinet/tcp.h>
    #include <sys/resource.h>
    
    //
    // This is used instead of -1, since the. by WinSock
    // On now linux EAGAIN and EWOULDBLOCK may be the same value 
    // connect 链接中, linux 是 EINPROGRESS,winds 是 WSAEWOULDBLOCK
    //
    typedef int socket_t;
    
    #define INVALID_SOCKET          (~0)
    #define SOCKET_ERROR            (-1)
    
    // socket_init - 初始化 socket 库初始化方法
    inline void socket_init(void) {
        // 管道破裂, 忽略 SIGPIPE 信号
        signal(SIGPIPE, SIG_IGN);
    }
    
    inline int socket_close(socket_t s) {
        return close(s);
    }
    
    // socket_set_block     - 设置套接字是阻塞
    // socket_set_nonblock  - 设置套接字是非阻塞
    inline int socket_set_block(socket_t s) {
        int mode = fcntl(s, F_GETFL, 0);
        return fcntl(s, F_SETFL, mode & ~O_NONBLOCK);
    }
    
    inline int socket_set_nonblock(socket_t s) {
        int mode = fcntl(s, F_GETFL, 0);
        return fcntl(s, F_SETFL, mode | O_NONBLOCK);
    }
    
    // socket_recv      - 读取数据
    // socket_send      - 写入数据
    inline int socket_recv(socket_t s, void * buf, int sz) {
        return (int)read(s, buf, sz);
    }
    
    inline int socket_send(socket_t s, const void * buf, int sz) {
        return (int)write(s, buf, sz);
    }
    
    #endif
    
    #ifdef _MSC_VER
    
    #include <ws2tcpip.h>
    
    #undef  errno
    #define errno                   WSAGetLastError()
    #undef  strerror
    #define strerror                ((char * (*)(int))strerr)
    
    #undef  EINTR
    #define EINTR                   WSAEINTR
    #undef  EAGAIN
    #define EAGAIN                  WSAEWOULDBLOCK
    #undef  EINPROGRESS
    #define EINPROGRESS             WSAEWOULDBLOCK
    
    /*
     * WinSock 2 extension -- manifest constants for shutdown()
     */
    #define SHUT_RD                 SD_RECEIVE
    #define SHUT_WR                 SD_SEND
    #define SHUT_RDWR               SD_BOTH
    
    #define SO_REUSEPORT            SO_REUSEADDR
    
    typedef SOCKET socket_t;
    typedef int socklen_t;
    
    //
    // gettimeofday - Linux sys/time.h 中得到微秒时间实现
    // tv       : 返回结果包含秒数和微秒数
    // tz       : 包含的时区, winds 上这个变量没有作用
    // return   : 默认返回 0
    //
    extern int gettimeofday(struct timeval * tv, void * tz);
    
    //
    // strerr - linux 上替代 strerror, winds 替代 FormatMessage 
    // error    : linux 是 errno, winds 可以是 WSAGetLastError() ... 
    // return   : system os 拔下来的提示常量字符串
    //
    extern const char * strerr(int err);
    
    // socket_init - 初始化 socket 库初始化方法
    inline void socket_init(void) {
        WSADATA wsad;
        WSAStartup(WINSOCK_VERSION, &wsad);
    }
    
    // socket_close     - 关闭上面创建后的句柄
    inline int socket_close(socket_t s) {
        return closesocket(s);
    }
    
    // socket_set_block     - 设置套接字是阻塞
    // socket_set_nonblock  - 设置套接字是非阻塞
    inline int socket_set_block(socket_t s) {
        u_long mode = 0;
        return ioctlsocket(s, FIONBIO, &mode);
    }
    
    inline int socket_set_nonblock(socket_t s) {
        u_long mode = 1;
        return ioctlsocket(s, FIONBIO, &mode);
    }
    
    // socket_recv      - 读取数据
    // socket_send      - 写入数据
    inline int socket_recv(socket_t s, void * buf, int sz) {
        return sz > 0 ? recv(s, buf, sz, 0) : 0;
    }
    
    inline int socket_send(socket_t s, const void * buf, int sz) {
        return send(s, buf, sz, 0);
    }
    
    #endif
    
    //
    // 通用 sockaddr_in ipv4 地址
    //
    typedef struct sockaddr_in sockaddr_t[1];
    
    // socket_dgram     - 创建 UDP socket
    // socket_stream    - 创建 TCP socket
    inline socket_t socket_dgram(void) {
        return socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    }
    
    inline socket_t socket_stream(void) {
        return socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    }
    
    // socket_set_reuse - 开启端口和地址复用
    // socket_set_keepalive - 开启心跳包检测, 默认2h 5次
    inline int socket_set_enable(socket_t s, int optname) {
        int ov = 1;
        return setsockopt(s, SOL_SOCKET, optname, (void *)&ov, sizeof ov);
    }
    
    inline int socket_set_reuse(socket_t s) {
        return socket_set_enable(s, SO_REUSEPORT);
    }
    
    inline int socket_set_keepalive(socket_t s) {
        return socket_set_enable(s, SO_KEEPALIVE);
    }
    
    // socket_set_rcvtimeo - 设置接收数据毫秒超时时间
    // socket_set_sndtimeo - 设置发送数据毫秒超时时间
    inline int socket_set_time(socket_t s, int ms, int optname) {
        struct timeval ov = { 0,0 };
        if (ms > 0) {
            ov.tv_sec = ms / 1000;
            ov.tv_usec = (ms % 1000) * 1000;
        }
        return setsockopt(s, SOL_SOCKET, optname, (void *)&ov, sizeof ov);
    }
    
    inline int socket_set_rcvtimeo(socket_t s, int ms) {
        return socket_set_time(s, ms, SO_RCVTIMEO);
    }
    
    inline int socket_set_sndtimeo(socket_t s, int ms) {
        return socket_set_time(s, ms, SO_SNDTIMEO);
    }
    
    // socket_get_error - 得到当前socket error 值, 0 表示正确, 其它都是错误
    inline int socket_get_error(socket_t s) {
        int err;
        socklen_t len = sizeof(err);
        int r = getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&err, &len);
        return r < 0 ? errno : err;
    }
    
    // socket_recvfrom  - recvfrom 接受函数
    // socket_sendto    - sendto 发送函数
    inline int socket_recvfrom(socket_t s, void * buf, int len, int flags, sockaddr_t in) {
        socklen_t inlen = sizeof (sockaddr_t);
        return recvfrom(s, buf, len, flags, (struct sockaddr *)in, &inlen);
    }
    
    inline int socket_sendto(socket_t s, const void * buf, int len, int flags, const sockaddr_t to) {
        return sendto(s, buf, len, flags, (const struct sockaddr *)to, sizeof(sockaddr_t));
    }
    
    //
    // socket_recvn     - socket 接受 sz 个字节
    // socket_sendn     - socket 发送 sz 个字节
    //
    extern int socket_recvn(socket_t s, void * buf, int sz);
    extern int socket_sendn(socket_t s, const void * buf, int sz);
    
    // socket_bind          - bind    绑定函数
    // socket_listen        - listen  监听函数
    // socket_accept        - accept  等接函数
    // socket_connect       - connect 链接函数
    inline int socket_bind(socket_t s, const sockaddr_t addr) {
        return bind(s, (const struct sockaddr *)addr, sizeof(sockaddr_t));
    }
    
    inline int socket_listen(socket_t s) {
        return listen(s, SOMAXCONN);
    }
    
    inline socket_t socket_accept(socket_t s, sockaddr_t addr) {
        socklen_t len = sizeof (sockaddr_t);
        return accept(s, (struct sockaddr *)addr, &len);
    }
    
    inline int socket_connect(socket_t s, const sockaddr_t addr) {
        return connect(s, (const struct sockaddr *)addr, sizeof(sockaddr_t));
    }
    
    //
    // socket_binds     - 端口绑定返回绑定好的 socket fd, 返回 INVALID_SOCKET or PF_INET PF_INET6
    // socket_listens   - 端口监听返回监听好的 socket fd.
    //
    extern socket_t socket_binds(const char * ip, uint16_t port, uint8_t protocol, int * family);
    extern socket_t socket_listens(const char * ip, uint16_t port, int backlog);
    
    
    //
    // socket_addr -socket_recv 通过 ip, port 构造 ipv4 结构
    //
    extern int socket_addr(const char * ip, uint16_t port, sockaddr_t addr);
    
    // socket_pton - 返回 ip 串
    inline char * socket_pton(sockaddr_t addr, char ip[INET_ADDRSTRLEN]) {
        return (char *)inet_ntop(AF_INET, &addr->sin_addr, ip, INET_ADDRSTRLEN);
    }
    
    //
    // socket_host - 通过 ip:port 串得到 socket addr 结构
    // host     : ip:port 串
    // addr     : 返回最终生成的地址
    // return   : >= EBase 表示成功
    //
    extern int socket_host(const char * host, sockaddr_t addr);
    
    //
    // socket_tcp - 创建 TCP 详细套接字
    // host     : ip:port 串  
    // return   : 返回监听后套接字
    //
    extern socket_t socket_tcp(const char * host);
    
    //
    // socket_udp - 创建 UDP 详细套接字
    // host     : ip:port 串  
    // return   : 返回绑定后套接字
    //
    extern socket_t socket_udp(const char * host);
    
    //
    // socket_connects - 返回链接后的阻塞套接字
    // host     : ip:port 串  
    // return   : 返回链接后阻塞套接字
    //
    extern socket_t socket_connects(const char * host);
    
    //
    // socket_connectos - 返回链接后的非阻塞套接字
    // host     : ip:port 串  
    // ms       : 链接过程中毫秒数
    // return   : 返回链接后非阻塞套接字
    //
    extern socket_t socket_connectos(const char * host, int ms);
    
    #endif//_H_SOCKET

    socket.c - https://github.com/wangzhione/structc/blob/master/structc/system/socket.c

    #include "socket.h"
    
    #ifdef _MSC_VER
    
    //
    // gettimeofday - Linux sys/time.h 中得到微秒时间实现
    // tv       : 返回结果包含秒数和微秒数
    // tz       : 包含的时区, winds 上这个变量没有作用
    // return   : 默认返回 0
    //
    int 
    gettimeofday(struct timeval * tv, void * tz) {
        struct tm m;
        SYSTEMTIME se;
    
        GetLocalTime(&se);
        m.tm_year = se.wYear - 1900;
        m.tm_mon = se.wMonth - 1;
        m.tm_mday = se.wDay;
        m.tm_hour = se.wHour;
        m.tm_min = se.wMinute;
        m.tm_sec = se.wSecond;
        m.tm_isdst = -1; // 不考虑夏令时
    
        tv->tv_sec = (long)mktime(&m);
        tv->tv_usec = se.wMilliseconds * 1000;
    
        return 0;
    }
    
    #endif
    
    //
    // socket_recvn     - socket 接受 sz 个字节
    // socket_sendn     - socket 发送 sz 个字节
    //
    
    int 
    socket_recvn(socket_t s, void * buf, int sz) {
        int r, n = sz;
        while (n > 0) {
            r = recv(s, buf, n, 0);
            if (r == 0) break;
            if (r == SOCKET_ERROR) {
                if (errno == EINTR)
                    continue;
                return SOCKET_ERROR;
            }
            n -= r;
            buf = (char *)buf + r;
        }
        return sz - n;
    }
    
    int 
    socket_sendn(socket_t s, const void * buf, int sz) {
        int r, n = sz;
        while (n > 0) {
            r = send(s, buf, n, 0);
            if (r == 0) break;
            if (r == SOCKET_ERROR) {
                if (errno == EINTR)
                    continue;
                return SOCKET_ERROR;
            }
            n -= r;
            buf = (char *)buf + r;
        }
        return sz - n;
    }
    
    //
    // socket_addr - 通过 ip, port 构造 ipv4 结构
    //
    int 
    socket_addr(const char * ip, uint16_t port, sockaddr_t addr) {
        addr->sin_family = AF_INET;
        addr->sin_port = htons(port);
        addr->sin_addr.s_addr = inet_addr(ip);
        if (addr->sin_addr.s_addr == INADDR_NONE) {
            struct hostent * host = gethostbyname(ip);
            if (!host || !host->h_addr)
                return EParam;
    
            // 尝试一种, 默认 ipv4
            memcpy(&addr->sin_addr, host->h_addr, host->h_length);
        }
        memset(addr->sin_zero, 0, sizeof addr->sin_zero);
    
        return SBase;
    }
    
    //
    // socket_binds     - 端口绑定返回绑定好的 socket fd, 返回 INVALID_SOCKET or PF_INET PF_INET6
    // socket_listens   - 端口监听返回监听好的 socket fd.
    //
    socket_t 
    socket_binds(const char * ip, uint16_t port, uint8_t protocol, int * family) {
        socket_t fd;
        char ports[sizeof "65535"];
        struct addrinfo * addr = NULL, hint = { 0 };
        if (NULL == ip || *ip == '')
            ip = "0.0.0.0"; // default INADDR_ANY
    
        sprintf(ports, "%hu", port);
        hint.ai_family = AF_UNSPEC;
        if (protocol == IPPROTO_TCP)
            hint.ai_socktype = SOCK_STREAM;
        else {
            assert(protocol == IPPROTO_UDP);
            hint.ai_socktype = SOCK_DGRAM;
        }
        hint.ai_protocol = protocol;
    
        if (getaddrinfo(ip, ports, &hint, &addr))
            return INVALID_SOCKET;
    
        fd = socket(addr->ai_family, addr->ai_socktype, 0);
        if (fd == INVALID_SOCKET)
            goto err_free;
        if (socket_set_reuse(fd))
            goto err_close;
        if (bind(fd, addr->ai_addr, (int)addr->ai_addrlen))
            goto err_close;
    
        // Success return ip family
        if (family)
            *family = addr->ai_family;
        freeaddrinfo(addr);
        return fd;
    
    err_close:
        socket_close(fd);
    err_free:
        freeaddrinfo(addr);
        return INVALID_SOCKET;
    }
    
    socket_t 
    socket_listens(const char * ip, uint16_t port, int backlog) {
        socket_t fd = socket_binds(ip, port, IPPROTO_TCP, NULL);
        if (INVALID_SOCKET != fd && listen(fd, backlog)) {
            socket_close(fd);
            return INVALID_SOCKET;
        }
        return fd;
    }
    
    // host_parse - 解析 host 内容
    static int host_parse(const char * host, char ip[BUFSIZ], uint16_t * pprt) {
        int port = 0;
        char * st = ip;
        if (!host || !*host || *host == ':')
            strcpy(ip, "0.0.0.0");
        else {
            char c;
            // 简单检查字符串是否合法
            size_t n = strlen(host);
            if (n >= BUFSIZ)
                RETURN(EParam, "host err %s", host);
    
            // 寻找分号
            while ((c = *host++) != ':' && c)
                *ip++ = c;
            *ip = '';
            if (c == ':') {
                if (n > ip - st + sizeof "65535")
                    RETURN(EParam, "host port err %s", host);
                port = atoi(host);
                // 有些常识数字, 不一定是魔法 ... :)
                if (port <= 1024 || port > 65535)
                    RETURN(EParam, "host port err %s, %d", host, port);
            }
        }
    
        *pprt = port;
        return SBase;
    }
    
    //
    // socket_host - 通过 ip:port 串得到 socket addr 结构
    // host     : ip:port 串
    // addr     : 返回最终生成的地址
    // return   : >= EBase 表示成功
    //
    int 
    socket_host(const char * host, sockaddr_t addr) {
        uint16_t port; char ip[BUFSIZ];
        if (host_parse(host, ip, &port) < SBase)
            return EParam;
    
        // 开始构造 addr
        if (NULL == addr) {
            sockaddr_t nddr;
            return socket_addr(ip, port, nddr);
        }
        return socket_addr(ip, port, addr);
    }
    
    //
    // socket_tcp - 创建 TCP 详细套接字
    // host     : ip:port 串  
    // return   : 返回监听后套接字
    //
    socket_t 
    socket_tcp(const char * host) {
        uint16_t port; char ip[BUFSIZ];
        if (host_parse(host, ip, &port) < SBase)
            return EParam;
        return socket_listens(ip, port, SOMAXCONN);
    }
    
    //
    // socket_udp - 创建 UDP 详细套接字
    // host     : ip:port 串  
    // return   : 返回绑定后套接字
    //
    socket_t 
    socket_udp(const char * host) {
        uint16_t port; char ip[BUFSIZ];
        if (host_parse(host, ip, &port) < SBase)
            return EParam;
        return socket_binds(ip, port, IPPROTO_UDP, NULL);
    }
    
    //
    // socket_connects - 返回链接后的阻塞套接字
    // host     : ip:port 串  
    // return   : 返回链接后阻塞套接字
    //
    socket_t 
    socket_connects(const char * host) {
        sockaddr_t addr;
        socket_t s = socket_stream();
        if (INVALID_SOCKET == s) {
            RETURN(s, "socket_stream is error");
        }
    
        // 解析配置成功后尝试链接
        if (socket_host(host, addr) >= SBase)
            if (socket_connect(s, addr) >= SBase)
                return s;
    
        socket_close(s);
        RETURN(INVALID_SOCKET, "socket_connects %s", host);
    }
    
    //
    // socket_connecto      - connect 超时链接, 返回非阻塞 socket
    //
    static int socket_connecto(socket_t s, const sockaddr_t addr, int ms) {
        int n, r;
        struct timeval to;
        fd_set rset, wset, eset;
    
        // 还是阻塞的connect
        if (ms < 0) return socket_connect(s, addr);
    
        // 非阻塞登录, 先设置非阻塞模式
        r = socket_set_nonblock(s);
        if (r < SBase) return r;
    
        // 尝试连接, connect 返回 -1 并且 errno == EINPROGRESS 表示正在建立链接
        r = socket_connect(s, addr);
        // connect 链接中, linux 是 EINPROGRESS,winds 是 WSAEWOULDBLOCK
        if (r >= SBase || errno != EINPROGRESS) {
            socket_set_block(s);
            return r;
        }
    
        // 超时 timeout, 直接返回结果 EBase = -1 错误
        if (ms == 0) {
            socket_set_block(s);
            return EBase;
        }
    
        FD_ZERO(&rset); FD_SET(s, &rset);
        FD_ZERO(&wset); FD_SET(s, &wset);
        FD_ZERO(&eset); FD_SET(s, &eset);
        to.tv_sec = ms / 1000;
        to.tv_usec = (ms % 1000) * 1000;
        n = select((int)s + 1, &rset, &wset, &eset, &to);
        // 超时直接滚
        if (n <= 0) {
            socket_set_block(s);
            return EBase;
        }
    
        // 当连接成功时候,描述符会变成可写
        if (n == 1 && FD_ISSET(s, &wset)){
            socket_set_block(s);
            return SBase;
        }
    
        // 当连接建立遇到错误时候, 描述符变为即可读又可写
        if (FD_ISSET(s, &eset) || n == 2) {
            socklen_t len = sizeof n;
            // 只要最后没有 error 那就 链接成功
            if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&n, &len) && !n)
                r = SBase;
        }
        socket_set_block(s);
        return r;
    }
    
    //
    // socket_connectos - 返回链接后的非阻塞套接字
    // host     : ip:port 串  
    // ms       : 链接过程中毫秒数
    // return   : 返回链接后非阻塞套接字
    //
    socket_t 
    socket_connectos(const char * host, int ms) {
        sockaddr_t addr;
        socket_t s = socket_stream();
        if (INVALID_SOCKET == s) {
            RETURN(s, "socket_stream is error");
        }
    
        // 解析配置成功后尝试链接
        if (socket_host(host, addr) >= SBase)
            if (socket_connecto(s, addr, ms) >= SBase)
                return s;
    
        socket_close(s);
        RETURN(INVALID_SOCKET, "socket_connectos %s", host);
    }

    哪怕 winds, 设计思路也是仿照 linux socket 套路. 构建 socket 接口, 希望上面代码能说明什么是少林

    拳法, 千锤百练.  后面 base struct system 代码量不少, 难一一说. 喜欢的可以后续抄袭一次. (我也是

    抄袭别人而走入了编程的世界, 了解这分形的人生吧)

    正文 -  风吹草动

      通过引言前言认识了 structc 是什么样项目, 项目构建, 代码风格等. 这里准备说一下设计 structc

    项目初衷. 很久前写 C 代码, 发现数据结构确定后, 基本整个脉络就定下了. 所以想用 C 构建一个通用

    简单的数据结构库. 所以有了这个项目. 

      扯一点, 了解 structc 项目后能够为怎样技能加点. 例如学完 struct 目录, 数据结构可以轻松结课. 

    抄完 system 操作系统可以结课. base 清楚后, 框架中间件设计也算入门了. 了解整体布局后, 实战中的

    脚手架设计也不过如此. 但缺点也有, 见效慢. C 太老了, 想通过 C 看清编程源头, 不下时间是不现实的,

    幸运的是最终收获 -> 哈哈 -> 怎么脱发又严重了 .... 

    比如 atom.h - https://github.com/wangzhione/structc/blob/master/structc/system/atom.h

    #ifndef _H_ATOM
    #define _H_ATOM
    
    #include "atomic.h"
    
    //
    // atom_t 自旋锁类型
    // [static] atom_t o = 0;
    //   atom_lock(o);
    //  - One Man RPG
    // atom_unlock(o);
    //
    typedef volatile long atom_t;
    
    // atom_acquire - 维护优化后读写代码不在其前
    #define atom_acquire()      atomic_fence(ATOMIC_ACQUIRE)
    // atom_release - 维护优化后读写代码不在其后
    #define atom_release()      atomic_fence(ATOMIC_RELEASE)
    // atom_seq_cst - 维护优化后读写代码前后不动
    #define atom_seq_cst()      atomic_fence(ATOMIC_SEQ_CST)
    
    #ifdef __GNUC__
    
    #define atom_trylock(o)     (!__sync_lock_test_and_set(&(o), 1))
    
    #define atom_lock(o)        while(__sync_lock_test_and_set(&(o), 1))
    
    #define atom_unlock(o)      __sync_lock_release(&(o))
    
    // 内存屏障, 维持代码顺序
    #define atom_sync()         __sync_synchronize()
    
    // v += a ; return v;
    #define atom_add(v, a)      __sync_add_and_fetch(&(v), (a))
    // type tmp = v ; v = a; return tmp;
    #define atom_set(v, a)      __sync_lock_test_and_set(&(v), (a))
    // v &= a; return v;
    #define atom_and(v, a)      __sync_and_and_fetch(&(v), (a))
    // return ++v;
    #define atom_inc(v)         __sync_add_and_fetch(&(v), 1)
    // return --v;
    #define atom_dec(v)         __sync_sub_and_fetch(&(v), 1)
    // bool b = v == c; b ? v=a : ; return b;
    #define atom_cas(v, c, a)   __sync_bool_compare_and_swap(&(v), (c), (a))
    
    #endif
    
    #ifdef _MSC_VER
    
    #include <intrin.h>
    #include <intrin0.h>
    
    /* Interlocked intrinsic mapping for _nf/_acq/_rel */
    #if defined(_M_ARM) || defined(_M_ARM64)
    #define _ACQUIRE(x) ATOMIC_CONCAT(x, _acq)
    #else /* defined(_M_ARM) || defined(_M_ARM64) */
    #define _ACQUIRE(x) x
    #endif /* defined(_M_ARM) || defined(_M_ARM64) */
    
    #define atom_trylock(o)     (!_ACQUIRE(_interlockedbittestandset)(&(o), 0))
    
    #define atom_lock(o)        while(_ACQUIRE(_interlockedbittestandset)(&(o), 0))
    
    inline void store_release(atom_t * x) {
        /* store _Value atomically with release memory order */
    #if defined(_M_ARM) || defined(_M_ARM64)
        __dmb(0xB /* _ARM_BARRIER_ISH or _ARM64_BARRIER_ISH*/);
        __iso_volatile_store32((volatile int *)x, 0);
    #else
        _ReadWriteBarrier();
        *x = 0;
    #endif
    }
    
    #define atom_unlock(o)      store_release(&(o))
    
    // 保证代码优化后不乱序执行
    #define atom_sync()         MemoryBarrier()
    
    // v 和 a 都是 long 这样数据
    #define atom_add(v, a)      InterlockedAdd((volatile LONG *)&(v), (LONG)(a))
    #define atom_set(v, a)      InterlockedExchange((volatile LONG *)&(v), (LONG)(a))
    #define atom_and(v, a)      InterlockedAnd((volatile LONG *)&(v), (LONG)(a))
    #define atom_inc(v)         InterlockedIncrement((volatile LONG *)&(v))
    #define atom_dec(v)         InterlockedDecrement((volatile LONG *)&(v))
    //
    // 对于 InterlockedCompareExchange(v, c, a) 等价于下面
    // long tmp = v ; v == a ? v = c : ; return tmp;
    //
    // 咱们的 atom_cas(v, c, a) 等价于下面
    // long tmp = v ; v == c ? v = a : ; return tmp;
    //
    #define atom_cas(v, c, a)   ((LONG)(c) == InterlockedCompareExchange((volatile LONG *)&(v), (LONG)(a), (LONG)(c)))
    
    #endif
    
    #endif//_H_ATOM

    代码在改中变的有味道, 有态度. 当然更欢迎同行给予补充, 共同提高进步 ~ 

    毕竟错误是难免的 : )     

    后记 - 江湖再会

    金子 - https://music.163.com/#/song?id=376994

  • 相关阅读:
    Git工作中用法(Gitlab)
    面试记录,不定时更新中。。。
    webservice 技术改进
    HTTP详解
    Git学习05 --分支管理02
    Git学习04 --分支管理
    链表判环问题
    k-选取问题
    图片与字符串(base64编码)的转化
    二叉堆与堆排序的简单实现
  • 原文地址:https://www.cnblogs.com/life2refuel/p/9766875.html
Copyright © 2020-2023  润新知