如何在C中存储不同类型的数据?
我想存储一些数据,这些数据可以是只有在运行时才知道的不同类型的数据。我不想浪费任何内存,我想读取所有数据作为浮点值。在C++中我会做这样的事情< /P>如何在C中存储不同类型的数据?,c,polymorphism,virtual-functions,c11,C,Polymorphism,Virtual Functions,C11,我想存储一些数据,这些数据可以是只有在运行时才知道的不同类型的数据。我不想浪费任何内存,我想读取所有数据作为浮点值。在C++中我会做这样的事情< /P> struct IA { virtual float value(int index) = 0; }; template<class T> struct A : public IA { A(T* bytes, uint32_t size) { values.resize(size); memcpy(valu
struct IA {
virtual float value(int index) = 0;
};
template<class T>
struct A : public IA {
A(T* bytes, uint32_t size) {
values.resize(size);
memcpy(values.data(), bytes, size*sizeof(T));
}
float value(int index) override {
return static_cast<float>(values[index]);
}
std::vector<T> values;
};
int main() {
uint16_t bytes[] = {1, 2, 3, 4, 5 };
IA *a = new A<uint16_t>(bytes, 5);
float value = a->value(0);
delete a;
}
struct IA{
虚拟浮点值(整数索引)=0;
};
模板
结构A:公共IA{
A(T*字节,uint32_T大小){
值。调整大小(大小);
memcpy(values.data(),字节,大小*sizeof(T));
}
浮点值(整型索引)覆盖{
返回静态_cast(值[索引]);
}
std::向量值;
};
int main(){
uint16_t字节[]={1,2,3,4,5};
IA*a=新的a(字节,5);
浮动值=a->值(0);
删除一条;
}
我的问题是如何用C++代替C++来完成这项工作。我可以将数据存储在uint8\t
数组中,并存储位的nr和有符号的值。然后,每次从数组中读取值时,都使用开关盒和强制转换。但是,如果阵列很大并且有很多读取,那么这将非常昂贵
我还希望能够传递数组,而无需执行任何特殊的if情况?您可以使用函数指针模拟虚拟方法:
typedef struct
{
void* userData;
float (*value)(void*, int);
} IA;
float get_float_from_uint16(void* userData, int index)
{
return ((uint16_t*) userData)[index];
}
int main()
{
uint16_t bytes[] = {1, 2, 3, 4, 5 };
IA a = {bytes, &get_float_from_uint16};
int index = 0;
float f = a.value(a.userData, index);
}
当然,这很简单(扰流板提示:不,不容易,很难)。我相信下面的代码和C++一样,但是用C语言编写。
那么会发生什么:
- 首先,我定义了
struct IA_s
,这是一个用于访问类似IA对象的接口。该结构仅包含用于访问对象的虚拟表。AI
对象有一个访问器IA\u value
,它的作用与代码中的IA::value
相同。我还将objects析构函数添加到虚拟表和作为析构函数的IA_fini
函数中。您的代码中缺少它,并且代码会泄漏内存
- 然后我创建了两个大宏
A_C
和A_H
A_H
用于头文件中A_C
用于源文件内部。它们为任何类型定义了对象的通用实现。第一个宏参数是用于为所有导出符号添加前缀的前缀。第二种是类型。A
对象包含一些类型元素的动态数组
- 然后我在
uint16\u t
type上用uint16\u
前缀实例化泛型宏
- 然后在main函数中,我为
A
对象分配内存,调用其构造函数,将其转换为IA
对象,获取第二个索引的值并调用所有析构函数
注:
- 构造函数被命名为
*\u init
,析构函数被命名为*\u fini
,所有与对象接触的函数都被命名为object.*
- 那很有趣
- 我想我永远不会在生产中使用这样的代码
- valgrind中释放的所有堆
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
/*IA对象------------------------------------------*/
//IA表格的正向定义
结构单元;
//对象的虚拟表
结构表{
浮动(*值)(结构IA_s*ctx,大小索引);
无效(*fini)(结构IA_s*ctx);
};
//结构,仅容纳vtable
结构图{
const struct IA_uvtable_us*vtable;
};
/**
*获取索引中保留的值
*@param t
*@返回值
*/
浮动IA_值(结构IA_s*t、大小索引){
断言(t!=NULL);
断言(t->vtable!=NULL);
断言(t->vtable->value!=NULL);
返回t->vtable->value(t,索引);
}
/**
*调用IA对象的析构函数
*/
void IA_fini(结构IA_s*t){
断言(t!=NULL);
断言(t->vtable!=NULL);
断言(t->vtable->fini!=NULL);
t->vtable->fini(t);
}
/*对象-------------------------------------------*/
/**
*对象的头模板
*/
#定义一个_H(名称、类型)\
/*对象*/\
结构名称##A#s{\
\
/*指向已分配数组的指针*/\
类型*arr\
\
/*数组中的元素。*/\
尺寸\
\
/*我想我们必须这样做。*/\
结构IA_s IA\
\
}; \
\
/*初始化一个对象。*字节被复制到对象*/\
/*@param t*/\
/*@patam bytes指向对象大小计数的指针*/\
/*@param size*/\
/*@返回负errno错误值,0成功*/\
int NAME###A#init(结构名##A#s*t,TYPE*字节,size#t size)\
\
/*取消初始化对象。*/\
无效名称##A#fini(结构名称##A#s*t)\
\
/*转换IA对象中的对象*/\
结构IA#U s*名称#A#U to#U IA(结构名称#A#U s*t);
//末日
#定义一个_C(名称、类型)\
\
/*A到IA接口----------------------------------------------------*/\
\
/*将指向IA的指针转换为指向A的指针*/\
静止的\
结构名称##A#s*名称##A#pnt(来自#ia#u s*t{\
return(void*)((unsigned char*)(void*)t)-offsetof(struct NAME#A#u s,ia))\
} \
\
/*这是我们在IA接口内的值函数*/\
静止的\
浮点名称##A u IA_值(结构IA _s*IA,大小索引){\
结构名称##A#s*t=NAME##A#u pnt_from_ia(ia)\
断言(t!=NULL)\
/*UB发生,如vector::operator[]*/\
断言(索引cnt)\
返回t->arr[索引]\
} \
\
/*这是IA接口内的析构函数*/\
静止的\
无效名称35;#A#u IA_fini(struct IA#u s*IA){\
结构名称##A#s*t=NAME##A#u pnt_from_ia(ia)\
断言(t!=NULL)\
姓名##A#fini(t)\
} \
\
静态常量结构IA_uvtable_35;u vtable_35;A_uia_uvtable={\
.value=名称##A#u IA_值\
.fini=名称##A#u IA#u fini\
}; \
\
int NAME##A#init(结构名##A#s*t,TYPE*字节,size_t cnt){\
断言(t!=NULL)\
断言(字节!=NULL)\
如果(cnt==0){\
/*malloc(0)恰好是不可移植的*/\
t->arr=NULL\
t->cnt=0\
返回0\
} \
如果(SIZE_MAX/sizeof(*t->arr)#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* IA object ---------------------------------------------- */
// forward definition for IA__vtable_s
struct IA_s;
// the virtual table of the object
struct IA__vtable_s {
float (*value)(struct IA_s *ctx, size_t index);
void (*fini)(struct IA_s *ctx);
};
// The structure, holds only the vtable
struct IA_s {
const struct IA__vtable_s *vtable;
};
/**
* Get the value holded in index index
* @param t
* @return the value
*/
float IA_value(struct IA_s *t, size_t index) {
assert(t != NULL);
assert(t->vtable != NULL);
assert(t->vtable->value != NULL);
return t->vtable->value(t, index);
}
/**
* Call the destructor of IA object
*/
void IA_fini(struct IA_s *t) {
assert(t != NULL);
assert(t->vtable != NULL);
assert(t->vtable->fini != NULL);
t->vtable->fini(t);
}
/* A object ---------------------------------------------- */
/**
* Header template of A object
*/
#define A_H(NAME, TYPE) \
/* A object */ \
struct NAME##A_s { \
\
/* pointer to allocated array */ \
TYPE *arr; \
\
/* elements in the array. */ \
size_t cnt; \
\
/* I think we have to have that. */ \
struct IA_s ia; \
\
}; \
\
/* Initializes A object. The *bytes are copied into the object */ \
/* @param t */ \
/* @patam bytes pointer to size count of objects */ \
/* @param size */ \
/* @returns negative errno value on error, 0 on success */ \
int NAME##A_init(struct NAME##A_s *t, TYPE *bytes, size_t size); \
\
/* Deinitializes A object. */ \
void NAME##A_fini(struct NAME##A_s *t); \
\
/* Converts A object in IA object */ \
struct IA_s *NAME##A_to_IA(struct NAME##A_s *t);
// end of A_H
#define A_C(NAME, TYPE) \
\
/* A to IA interface ----------------------------------------------------- */ \
\
/* Convert pointer to IA_s to pointer to A_s */ \
static \
struct NAME##A_s *NAME##A__pnt_from_ia(struct IA_s *t) { \
return (void*)( ((unsigned char*)(void *)t) - offsetof(struct NAME##A_s, ia) ); \
} \
\
/* this is our value function inside IA interface */ \
static \
float NAME##A__IA_value(struct IA_s *ia, size_t index) { \
struct NAME##A_s *t = NAME##A__pnt_from_ia(ia); \
assert(t != NULL); \
/* UB happens, as in case of vector::operator[] */ \
assert(index <= t->cnt); \
return t->arr[index]; \
} \
\
/* this is our destructor inside IA interface */ \
static \
void NAME##A__IA_fini(struct IA_s *ia) { \
struct NAME##A_s *t = NAME##A__pnt_from_ia(ia); \
assert(t != NULL); \
NAME##A_fini(t); \
} \
\
static const struct IA__vtable_s NAME##A__IA_vtable = { \
.value = NAME##A__IA_value, \
.fini = NAME##A__IA_fini, \
}; \
\
int NAME##A_init(struct NAME##A_s *t, TYPE *bytes, size_t cnt) { \
assert(t != NULL); \
assert(bytes != NULL); \
if (cnt == 0) { \
/* malloc(0) happens to be nonportable */ \
t->arr = NULL; \
t->cnt = 0; \
return 0; \
} \
if (SIZE_MAX / sizeof(*t->arr) < cnt) { \
/* multiplication overflow */ \
return -EOVERFLOW; \
} \
t->arr = malloc(cnt * sizeof(*t->arr)); \
if (t->arr == NULL) { \
return -ENOMEM; \
} \
t->cnt = cnt; \
memcpy(t->arr, bytes, cnt * sizeof(*t->arr)); \
t->ia.vtable = &NAME##A__IA_vtable; \
return 0; \
} \
void NAME##A_fini(struct NAME##A_s *t) { \
assert(t != NULL); \
free(t->arr); \
t->arr = NULL; \
t->cnt = 0; \
} \
struct IA_s *NAME##A_to_IA(struct NAME##A_s *t) { \
assert(t != NULL); \
return &t->ia; \
}
// A<uint16_t>
A_H(uint16_, uint16_t)
A_C(uint16_, uint16_t)
int main() {
uint16_t bytes[] = {1, 2, 3, 4, 5 };
// new()
struct uint16_A_s *temp = malloc(sizeof(*temp));
if (temp == NULL) {
// destructors
abort();
}
// A::A(bytes, 5)
int ret = uint16_A_init(temp, bytes, 5);
if (ret != 0) {
// destructors
free(temp);
abort();
}
// IA *ia = A;
struct IA_s *ia = uint16_A_to_IA(temp);
if (ia == NULL) {
// destructors
uint16_A_fini(temp);
free(temp);
abort();
}
// finally call IA::value(2)
float value = IA_value(ia, 2);
printf("%f\n", value);
// destructors
IA_fini(ia); // or uint16_A_fini(temp) depending on which you call delete on
free(temp);
}