You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让C函数同时处理float_vector和double_vector两种结构体类型?

适配float_vector和double_vector的通用函数实现方案

我正在编写OpenGL渲染相关的C代码,用动态数组实现线性代数功能,需要存储待渲染三角形的顶点坐标,现有大量使用这些动态数组(向量、矩阵)的代码,希望复用逻辑。现在遇到的问题是:要实现能同时操作float_vector和double_vector两种结构体的函数,两种结构体定义如下:

struct float_vector{
  int size_cap;
  int current_size;
  
  float *data;
};

struct double_vector{
  int size_cap;
  int current_size;
  
  double *data;
};

其中sum_vectors针对double_vector的实现示例如下:

int sum_vectors(struct double_vector *vector1, struct double_vector *vector2, struct double_vector *buffer){
  
  // Check if vectors, including buffer, are the same size
  if ((vector1->current_size != vector2->current_size)||(vector1->current_size != buffer->current_size)){
    fprintf(stderr, "ERROR: Vectors and buffer are not the same size");
    return -1;
  }

  // Main loop and calculations
  for (int i = 0; i < vector1->current_size; i++){
    buffer->data[i] = vector1->data[i] + vector2->data[i];
  }
  return 0;
}

我曾考虑使用_Generic但觉得调试难度高、可读性差;试过用union合并成单一vector结构体,但还是避免不了写重复逻辑的两个函数,想找合适的实现方式。


方法一:宏生成模板化代码

C没有原生模板,但可以用预处理器宏批量生成不同类型的函数实现,既复用逻辑又避免重复手写。针对sum_vectors可以这样写:

#define DEFINE_VECTOR_SUM(TYPE, TYPE_SUFFIX) \
int sum_vectors_##TYPE_SUFFIX(struct TYPE##_vector *vector1, struct TYPE##_vector *vector2, struct TYPE##_vector *buffer) { \
    if ((vector1->current_size != vector2->current_size) || (vector1->current_size != buffer->current_size)) { \
        fprintf(stderr, "ERROR: Vectors and buffer are not the same size"); \
        return -1; \
    } \
    for (int i = 0; i < vector1->current_size; i++) { \
        buffer->data[i] = vector1->data[i] + vector2->data[i]; \
    } \
    return 0; \
}

// 自动生成float和double版本的函数
DEFINE_VECTOR_SUM(float, float)
DEFINE_VECTOR_SUM(double, double)

预处理器会自动生成sum_vectors_float和sum_vectors_double两个函数,核心逻辑完全复用,调试时可以直接查看预编译后的代码,可读性和可维护性都不错。如果需要统一调用入口,可以用简单的_Generic做类型分发(仅做类型判断,核心逻辑还是宏生成的函数,调试难度低):

#define sum_vectors(v1, v2, buf) _Generic((v1), \
    struct float_vector*: sum_vectors_float, \
    struct double_vector*: sum_vectors_double)(v1, v2, buf)

方法二:重构结构体+类型标签统一

如果愿意调整现有结构体定义,可以给向量加一个类型标签,结合union存储不同类型的数据,实现统一的函数接口:

typedef enum {
    VECTOR_FLOAT,
    VECTOR_DOUBLE
} VectorType;

struct vector {
    int size_cap;
    int current_size;
    VectorType type;
    union {
        float* f_data;
        double* d_data;
    } data;
};

然后编写通用函数时,根据类型标签分支处理:

int sum_vectors(struct vector* vector1, struct vector* vector2, struct vector* buffer) {
    if ((vector1->current_size != vector2->current_size) || (vector1->current_size != buffer->current_size)) {
        fprintf(stderr, "ERROR: Vectors and buffer are not the same size");
        return -1;
    }
    if (vector1->type != vector2->type || vector1->type != buffer->type) {
        fprintf(stderr, "ERROR: Vectors and buffer must be the same type");
        return -1;
    }
    switch(vector1->type) {
        case VECTOR_FLOAT:
            for (int i = 0; i < vector1->current_size; i++) {
                buffer->data.f_data[i] = vector1->data.f_data[i] + vector2->data.f_data[i];
            }
            break;
        case VECTOR_DOUBLE:
            for (int i = 0; i < vector1->current_size; i++) {
                buffer->data.d_data[i] = vector1->data.d_data[i] + vector2->data.d_data[i];
            }
            break;
        default:
            fprintf(stderr, "ERROR: Unknown vector type");
            return -1;
    }
    return 0;
}

这种方式统一了结构体类型,调用时不需要区分float和double版本,但每次运算都要做类型判断,性能上有微小开销——不过对于OpenGL渲染场景,这个开销基本可以忽略。需要注意的是,现有代码中访问向量数据的逻辑需要同步修改。

方法三:void*封装通用逻辑(谨慎使用)

把向量的公共逻辑和类型相关逻辑拆分,用void*传递数据,同时传入元素大小和运算函数指针:

typedef void (*VectorAddFunc)(const void* a, const void* b, void* dest);

void add_float(const void* a, const void* b, void* dest) {
    *(float*)dest = *(float*)a + *(float*)b;
}

void add_double(const void* a, const void* b, void* dest) {
    *(double*)dest = *(double*)a + *(double*)b;
}

int sum_vectors_generic(void* vector1_data, void* vector2_data, void* buffer_data, int elem_size, VectorAddFunc add_func, int current_size) {
    if (current_size <= 0) {
        fprintf(stderr, "ERROR: Invalid vector size");
        return -1;
    }
    char* data1 = (char*)vector1_data;
    char* data2 = (char*)vector2_data;
    char* buf_data = (char*)buffer_data;
    for (int i = 0; i < current_size; i++) {
        add_func(data1 + i*elem_size, data2 + i*elem_size, buf_data + i*elem_size);
    }
    return 0;
}

调用时需要传入对应参数:

// float版本调用
sum_vectors_generic(vec_float1->data, vec_float2->data, buf_float->data, sizeof(float), add_float, vec_float1->current_size);
// double版本调用
sum_vectors_generic(vec_double1->data, vec_double2->data, buf_double->data, sizeof(double), add_double, vec_double1->current_size);

这种方式的缺点是丢失了编译期类型安全,容易传错参数,适合逻辑极度通用、支持类型极多的场景,对于你的OpenGL渲染需求,宏生成或结构体重构的方式更合适。


内容的提问来源于stack exchange,提问作者FlightSim_Enjoyer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 09:43:33