c - C 中的一次性伪通用 header

标签 c templates generics header-files c-preprocessor

在对通用 vector 进行一些研究后,我在 this question 上询问了相关问题,我想知道是否有任何方法可以检查库的每个实例是否只针对每种类型执行一次。

这是当前头文件的样子:

#ifndef VECTOR_GENERIC_MACROS
#define VECTOR_GENERIC_MACROS

    #ifndef TOKENPASTE
    #define TOKENPASTE(a, b) a ## b
    #endif

    #define vector_t(T) TOKENPASTE(vector_t_, T)

    #define vector_at(T) TOKENPASTE(*vector_at_, T)

    #define vector_init(T) TOKENPASTE(vector_init_, T)
    #define vector_destroy(T) TOKENPASTE(vector_destroy_, T)
    #define vector_new(T) TOKENPASTE(vector_new_, T)
    #define vector_delete(T) TOKENPASTE(vector_delete_, T)

    #define vector_push_back(T) TOKENPASTE(vector_push_back_, T)
    #define vector_pop_back(T) TOKENPASTE(vector_pop_back_, T)
    #define vector_resize(T) TOKENPASTE(vector_resize_, T)
    #define vector_reserve(T) TOKENPASTE(vector_reserve_, T)

#endif

typedef struct {
    size_t size;
    size_t capacity;
    TYPE *data;
} vector_t(TYPE);

inline TYPE vector_at(TYPE)(vector_t(TYPE) *vector, size_t pos);

void vector_init(TYPE)(vector_t(TYPE) *vector, size_t size);
void vector_destroy(TYPE)(vector_t(TYPE) *vector);
inline TYPE *vector_new(TYPE)(size_t size);
inline void vector_delete(TYPE)(vector_t(TYPE) *vector);

void vector_push_back(TYPE)(vector_t(TYPE) *vector, TYPE value);
inline TYPE vector_pop_back(TYPE)(vector_t(TYPE) *vector);
inline void vector_resize(TYPE)(vector_t(TYPE) *vector, size_t size);
void vector_reserve(TYPE)(vector_t(TYPE) *vector, size_t size);

然后可以将 header 与源定义一起包含在内:

#include <stdio.h>

#define TYPE int
#include "vector.h"
#include "vector.def"
#undef TYPE

int main()
{
    vector_t(int) myVectorInt;
    vector_init(int)(&myVectorInt, 0);

    for (int i = 0; i < 10; ++i)
        vector_push_back(int)(&myVectorInt, i);

    for (int i = 0; i < myVectorInt.size; ++i)
        printf("%d ", ++vector_at(int)(&myVectorInt, i));

    vector_destroy(int)(&myVectorInt);
    return 0;
}

我想确保每个 TYPE 只包含最后一个 endif 下面的内容。

显然,#ifdef VECTOR_INSTANCE(TYPE) 不起作用,所以我真的没有想法......

最佳答案

这是一个虽然问题,但是,当我问 similar question 时,我也对此事感兴趣前一段时间给你的。

我的结论是,如果您打算使用许多不同类型的 vector (或者,使用更准确的命名,动态数组),那么拥有所有这些函数是一种浪费 vector_##TYPE##_reserve()vector_##type##_resize() 等...多次。

相反,将这些函数在单独的 .c 文件中只定义一次,使用您的类型的大小作为额外参数,会更高效、更简洁。这些函数的原型(prototype)在单独的 .h 文件中。然后,相同的 .h 文件将提供为您自己的类型生成函数包装器的宏,这样您就不会看到它使用大小作为额外参数。

例如,您的 vector.h header 将包含以下内容:

/* Declare functions operating on a generic vector type */
void vector_generic_resize(void *vector, size_t size, size_t data_size);
void vector_generic_push_back(void *vector, void *value, size_t data_size);
void *vector_generic_pop_back(void *vector, size_t data_size);
void vector_generic_init(void *vector, size_t size, size_t data_size);
void vector_generic_destroy(void *vector) ; // I don't think data_size is needed here

/* Taken from the example in the question */
#define VECTOR_DEFINITION(type)\
typedef struct {\
    size_t size;\
    size_t capacity;\
    type *data;\
} vector_ ## type ## _t;\

/* Declare wrapper macros to make the above functions usable */
/* First the easy ones */
#define vector_resize(vector, size) vector_generic_resize(vector, size, sizeof(vector.data[0]))
#define vector_init(vector, size) vector_generic_init(vector, size, sizeof(vector.data[0]))
/* Type has to be given as an argument for the cast operator */
#define vector_pop_back(vector, type) (*(type*)(vector_generic_pop_back(vector, sizeof(vector.data[0]))))

/* This one is tricky, if 'value' is a constant, it's address cannot be taken.
I don't know if any better workarround is possible. */
#define vector_push_const(vector, type, value)                    \
{                                                                 \
    type temp = value;                                         \
    vector_generic_push_back(vector, &temp, sizeof(vector.data[0]));\
}

/* Equivalent macro, but for pushing variables instead of constants */
#define vector_push_var(vector, value) vector_generic_push_back(vector, &value, sizeof(vector.data[0]))

/* Super-macro rediriging to constant or variable version of push_back depending on the context */
#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define vector_push_back(...) GET_MACRO(__VA_ARGS__, vector_push_const, vector_push_var)(__VA_ARGS__)

/* This macro isn't really needed, but just for homogenity */
#define vector_descroy(vector) vector_generic_destroy(vector)

然后可以按照您在链接的示例中所说的那样使用函数,但 vector_generic_push_back 是一个重要的异常(exception),不幸的是,每次都必须将类型指定为额外的宏参数。

所以有了这个解决方案

  • 您只需在 .c 文件中执行 VECTOR_DEFINITION(),避免使用相同类型声明它两次的风险
  • vector 库在二进制文件中只存在一次
  • 除了 pop back 宏和 push literal 宏之外,无需在名称中使用类型即可优雅地使用这些宏。
  • 如果这是一个问题,您可以让 push literal use long long always,它会起作用,但可能会降低效率。
  • 类似地,您可以使 pop_back() 宏和 vector_generic_pop_back() 函数不返回任何内容,就像它们在 C++ 语言中所做的那样,这样如果您同时执行这两个技巧,就永远不需要使用宏中明确的类型名称。

作为引用,您在问题中链接的示例中发布的主要功能必须像这样进行调整:

    #include <stdio.h>
    #include <stdlib.h>
    #include "vector.h"

    typedef unsigned int uint;
    typedef char* str;

    VECTOR_DEFINITION(uint)
    VECTOR_DEFINITION(str)

    int main()
    {
        vector_uint_t vector;
        vector_init(&vector, 10);

        for (unsigned int i = 0; i < vector.size; ++i)
            vector.data[i] = i;

        for (unsigned int i = 0; i < 10; ++i)
            vector_push_back(&vector, i);

        /* When pushing back a constant, we *have* to specity the type */
        /* It is OK to use C keywords as they are supressed by the preprocessor */
        vector_push_back(&vector, unsigned int, 12);

        for (unsigned int i = 0; i < vector.size; ++i)
            printf("%d ", vector.data[i]);
        printf("\n");

        vector_destroy(&vector);

        vector_str_t sentence;
        vector_init(&sentence, 0);

        vector_push_back(&sentence, "Hello");
        vector_push_back(&sentence, str, "World!");  /* Also possible, less efficient */
        vector_push_back(&sentence, "How");
        vector_push_back(&sentence, "are");
        vector_push_back(&sentence, "you?");

        for (unsigned int i = 0; i < sentence.size; ++i)
            printf("%s ", sentence.data[i]);
        printf("\n");

        vector_destroy(&sentence);

        return 0;
    }

关于c - C 中的一次性伪通用 header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29052877/

相关文章:

Java:使用泛型进行列表初始化(T 扩展 Enum<T> 和 AnyInterface)

java - 如何制作按字母顺序对集合进行排序的通用函数

c - 操作数如何提升

c - 指针和整数之间的警告比较

c++ - 如何使 is_pod<T> 测试在编译期间而不是执行期间执行?

c++ - 复制迭代器传递的容器

java - 收集参数以应用于 Java/Scala 中的柯里化(Currying)函数

c - 将 bc 计算器输出管道返回给父 fork()

c++ - printf() 在源代码中不起作用?

c++ - 函数专用模板问题