C中的一次性伪通用头文件 [英] Once-only pseudo-generic header in C

查看:171
本文介绍了C中的一次性伪通用头文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在对通用向量进行了一些工作之后,我询问了关于这个问题,我想知道是否有任何方法检查图书馆的每个实例是每种类型只做一次。

After some work on the generic vector I asked about on this question, I would like to know if there is any way of checking that each instanciation of the library is only done once per type.

这是什么当前的头文件如下所示:

Here is what the current header file looks like:

#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);

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

The header can then be included along with the source definitions:

#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;
}

我想确保只包含最后endif下方的内容每个类型一次。

I would like to make sure that the content below that last endif is only included once per TYPE.

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

Obviously, #ifdef VECTOR_INSTANCE(TYPE) does not work, so I'm really out of ideas...

推荐答案

这是一个疑问,但是,当我询问类似的问题在前一段时间。

It's a though question, however, I was also interested in the matter when I asked a similar question to yours some time ago.

我的结论是,如果你打算使用向量(或者,使用更准确的命名,动态数组)的许多不同类型,那么浪费所有这些函数 vector _ ## TYPE ## _ reserve() vector _ ## type ## _ resize(),etc ...多次。

My conclusions is that if you are going to use vectors (or, using more accurate naming, dynamic arrays) of many different types then it's wasteful to have all those functions vector_##TYPE##_reserve(), vector_##type##_resize(), etc... multiple times.

相反,它更高效且更清洁使这些函数在单独的 .c 文件中仅定义一次,并将您的类型的大小用作额外的参数。这些函数在单独的 .h 文件中原型化。然后,相同的 .h 文件将提供为您自己的类型生成函数包装的宏,以便您不会将大小看作额外的参数。

Instead, it is more efficient and clean to have those functions defined only once in a separate .c file, using your type's size as an extra argument. Those functions prototyped in a separate .h file. Then the same .h file would provide macros that generate functions wrappers for your own types, so that you don't see it using the size as an extra argument.

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

For example, your vector.h header would contain the following :

/* 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的显着例外不幸的是类型必须是每次指定为一个额外的宏参数。

The functions can then be used as you said in the example you linked, with the significant exception of vector_generic_push_back where unfortunately the type has to be specified each time as an extra macro argument.

所以使用这个解决方案


  • 您只需在 .c VECTOR_DEFINITION() >文件,避免了使用相同类型两次声明它的风险。

  • 矢量库在二进制文件中只存在一次。

  • 宏可以是使用优雅而不使用名称中的类型,除了回弹宏和推文字宏。

  • 如果这是一个问题,您可以使推文字总是使用long long,可能会工作,但可能会降低效率。同样,您可以使pop_back()宏和 vector_generic_pop_back()函数不会返回任何类似它们以C ++语言完成,所以如果你同时使用这两种技巧,你就不需要在宏中显式地使用类型名称。

  • You only have to do VECTOR_DEFINITION() within the .c file, avoiding the risk of declaring it with the same type twice
  • The vector library is only existing once in the binary
  • The macros can be used elegantly without using the type in their names, except for the pop back macro and the push literal macro.
  • If this is a problem you could make the push literal use long long always, it will work but potentially loose efficiency.
  • Similarly you could make the pop_back() macro and the vector_generic_pop_back() functions not return anything like they does in the C++ language, so that if you do both of those tricks you never need to use the type name explicitly in the macros.

作为参考,您在问题中链接的示例中发布的主要功能必须如此调整:

As a reference, the main function you posted in the example that is linked in your question has to be adapted like that :

    #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中的一次性伪通用头文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆