# C 语言重载


很多时候我在思考 C 语言 和 CPP 一样允许函数重载,调用函数会方便很多.
我去翻了很多 源码 和 文档,算是找到了一些 类似的方法.
由于 C 语言 的底层 实现与 CPP 不同,所以不可能实现百分百的重载.
C 语言的 POSIX 标准 的 open() 函数就实现了重载,大概率是这样的实现的 —— 依赖函数参数

# 方法一:

运用函数指针,指向函数实体, 而 函数名 由宏定义实现,例子如下
typedef struct CC{
    int a;
    int b;
}CC;
void func_int(void *a)
{
    printf("%d\n",*(int*)a);
}
void func_double(void *a)
{
    printf("%d\n",*(int*)a);
}
void func_int(void *a)
{
    printf("%d\t",((CC*)a)->a);
    printf("%d\n",((CC*)a)->b);
}
typedef void (*ptr)(void *);
void c_func(ptr fun_ptr, void *param)
{
    fun_ptr(param); // 调用对应函数
}
int main()
{
    int a = 23;
    double b = 23.23;
    CC CA = {10, 11};
    c_func(func_int, &a);       // 整数
    c_func(func_double, &b);    // 浮点
    c_func(func_struct, &CA);   // 结构体
    return 0;
}

优点是 实现起来很简单,不需要外加头文件
这个方法的问题很明显,我们仍然需要记住相关函数的名称,而且函数的参数个数也受到相应的限制

# 方法二:

使用可变参数类型 `va_list` 来获取 从(...) 输入的所有参数,  通过第二个参数来决定使用哪一个函数

例子如下:

void va_overload2(int p1, int p2)
{
    printf("va_overload2 %d %d\n", p1, p2);
}
void va_overload3(int p1, int p2, int p3)
{
    printf("va_overload3 %d %d %d\n", p1, p2, p3);
}
void va_overload4(int p1, int p2, int p3, int p4)
{
    printf("va_overload4 %d %d %d %d\n", p1, p2, p3, p4);
}
static void va_overload(int p1, int p2, ...)
{
    switch (p2)
    {
    case 2:
    {
        va_list v;
        va_start(v, p2);
        int p3 = va_arg(v, int);
        va_end(v);
        va_overload3(p1, p2, p3);
        break;
    }
    case 3:
    {
        va_list v;
        va_start(v, p2);
        int p3 = va_arg(v, int);
        int p4 = va_arg(v, int);
        va_end(v);
        va_overload4(p1, p2, p3, p4);
        break;
    }
    default:
    {
        va_overload2(p1, p2);
        break;
    }
    }
}

# 方法三

前面三种,说是重载其实更像是一般的条件语句. 下面这种就略微像 CPP 的函数重载
模拟了 CPP 重载函数 底层编译过程, 对函数进行重命名
// 粘合宏,用于 函数粘合函数名称
#define CAT(A, B) CAT2(A, B)
#define CAT2(A, B) A##B
#define c_overload(...)                     \
    CAT(overload, COUNT_PARMS(__VA_ARGS__)) \
    (__VA_ARGS__)
#define COUNT_PARMS(...) \
    COUNT_PARMS2(0, ##__VA_ARGS__, PARMS_N_RESQ())
#define COUNT_PARMS2(...) \
    COUNT_PARM3(__VA_ARGS__)
// 最大 参数列表,可调 目前为 (0 ~ 63)
#define COUNT_PARM3(                                  \
    _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10,      \
    _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
    _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
    _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
    _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
    _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
    _61, _62, _63, N, ...) N
#define PARMS_N_RESQ()                          \
    63, 62, 61, 60,                             \
        59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \
        49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
        39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \
        29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
        19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
        9, 8, 7, 6, 5, 4, 3, 2, 1, 0
void overload1(int p1)
{
    printf("CPP One param: %d\n", p1);
}
void overload2(double *p1, const char *p2)
{
    printf("CPP Two params: %p (%f) %s\n", p1, *p1, p2);
}
void overload3(int p1, int p2, int p3)
{
    printf("CPP Three params: %d %d %d\n", p1, p2, p3);
}

方法三 通过 COUNT_PARMS 来 数出参数个数,然后使用 ## 来 将 overload 和 得到的参数个数粘合.
得到函数名 overloadx , 从而实现 "重载"