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 cast
doubleto
void*, but you can cast
double*to
void*` .
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.
1条答案
按热度按时间szqfcxe21#
你需要改变你的库来存储任意类型,主要的是它需要知道元素的大小,这样它就可以获取任何类型的地址,并使用
memcpy
来复制字节。例如:
不要忘记为
malloc
和realloc
的所有调用添加错误检查。