c语言中double和complex类型的泛型问题

6mw9ycah  于 2022-12-02  发布在  其他
关注(0)|答案(1)|浏览(182)

In the c programming language, there are no such things known as generics, but you can cheat your way of working with multiple types with macros and void pointers. For example lets say that we want a structure, called wrapper that simply stores a value of any type and any operations done, on the wrapper don't require anything to do with its value.

Using Macros

#define WRAPPER(TYPE) typedef struct {##TYPE value;} wrapper_##TYPE

Then the user can just paste WRAPPER(int); on top of their code and they will have access to wrapper_int or even paste it multiple times with different types.

Using Void*

typedef struct {void* value;} wrapper;

Now the user can cast their value to a void* and lets say they want to use an int . They need to cast their int to a void* pointer. This is how functions such as malloc , realloc and calloc work.

Problems

I'm creating a library for many things and one thing that I am currently creating is a dynamic array that works on any type and is also automatically freed.
To automatically free the arrays. I have created static global variables, one is an array storing the array and the other is the length of that array. Then using gnu c __attribute__((constructor)) , I am freeing both the arrays and the array containing the arrays.
This approach simply doesn't allow me to use the macro way of doing things, since all code needs to be defined in the library c file and not even the header.
So stuck to the void* solution. It works for many different types, even including strings, but float , double and complex types(structures, unions and enums) don't work, since you can't cast them to a void*. I read serval stackoverflow questions about the same question. In general, I found out that you can't castdoubletovoid*, but you can castdouble*tovoid*` .
Using just an int.

#include "kstring.h"
#include "array.h"

int main(int argc, const string* argv)
{
    array a = create(0);

    for (usize i = 1; i < argc; i++)
    {
        append(&a, (type)(usize)(int)parse(argv[i]));
    }

    for (usize i = 0; i < a.length; i++)
        print("%d\n", (int)(usize)(a.elements[i]));
}
./build/app 1 2 3 2 1
1
2
3
2
1

It even works on strings.

#include "kstring.h"
#include "array.h"

int main(int argc, const string* argv)
{
    array a = create(0);

    for (usize i = 1; i < argc; i++)
    {
        append(&a, (type)argv[i]);
    }

    for (usize i = 0; i < a.length; i++)
        print("%s\n", (string)(a.elements[i]));
}
./build/app X Y Z Y X
X
Y
Z
Y
X

But not on doubles, or any of the types mentioned.

#include "kstring.h"
#include "array.h"

int main(int argc, const string* argv)
{
    array a = create(0);

    for (usize i = 1; i < argc; i++)
    {
        double x = parse(argv[i]);
        append(&a, (type)&x);
    }

    for (usize i = 0; i < a.length; i++)
        print("%lf\n", *((double*)(a.elements[i])));
}
./build/app 1.1 2.1 3.3 1.2 1.1
1.100000
1.100000
1.100000
1.100000
1.100000

This is due, to the fact that it's storing the pointer to x and with each iteration the value is changing. So the value is what ever the latest value. How can I make this work?
type is just an alias to void* . Most of the code is using stuff from kstring.h and array.h which are defined by myself. All functions such as parse and ... work correctly.
The problem is not from my library, rather how to work with my libraries' generic system. I've seen the source code of multiple other libraries and they were doing the same thing.

szqfcxe2

szqfcxe21#

你需要改变你的库来存储任意类型,主要的是它需要知道元素的大小,这样它就可以获取任何类型的地址,并使用memcpy来复制字节。
例如:

struct array {
    int length;
    int esize;
    unsigned char *elements;
};

struct array create(int esize, length)
{
    struct array a;
    a.length = length;
    a.esize = esize;
    a.elements = malloc(esize * length);
    return a;
}

void append(struct array *a, const void *data)
{
    a->length++;
    a->elements = realloc(a->elements, a->esize * a->length);
    memcpy(a->elements + (a->esize * (a->length - 1)), data, a->esize);
}

void *get(struct array *a, int idx)
{
    return a->elements + (a->esize * idx);
}

不要忘记为mallocrealloc的所有调用添加错误检查。

相关问题