我了解到ADT是一个重要的概念,并且我正在学习这项技术。
这是我不知道该如何处理的问题。
Payload_Manager.h
typedef struct __attribute__((__packed__))
{
u32 Addr;
u16 Cmd;
u16 Len;
u8 Data[0];
}ATEIS_Payload_s; //payload
Payload_Manager.c
#include "Payload_Manager.h"
void* Payload_Manager_New(int size)
{
return (ATEIS_Payload_s*)malloc(size);
}
void* Payload_Manager_Ctor(void* _this, u32 ip, u16 cmdID, u16 dataLen, char* rxBuf)
{
ATEIS_Payload_s* this = (ATEIS_Payload_s*)_this;
this->Addr = ip;
this->Cmd = cmdID;
this->Len = dataLen;
memcpy(this->Data, rxBuf, dataLen);
return this;
}
void* Payload_Manager_Dtor(void** _this)
{
free(*_this);
*_this = NULL;
return *_this;
}
DNM_Manager.h
void* DNMManager_Ctor(void* _this,
void* name,
u32 ip,
u32 sn,
u32 subMask);
DNM_Manager.c
typedef struct
{
u32 Addr;
u32 SerialNo;
u32 SubnetMask;
char Name[NAME_SIZE];
}DNM;
static DNM DNMSet[SET_SIZE];
static DNM DNMTemp;
void* DNMManager_Ctor(void* _this,
void* name,
u32 ip,
u32 sn,
u32 subMask)
{
DNM* this = (DNM*)_this;
memcpy(this->Name, name, NAME_SIZE);
this->Addr = ip;
this->SerialNo = sn;
this->SubnetMask = subMask;
return this;
}
CmdHndlr.c
#include "Payload_Manager.h"
#include "DNM_Manager.h"
int main(void){
ATEIS_Payload_s* pl_p = NULL;
void* DNM_temp = NULL;
pl_p = OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msgSize, &ts, &err); //wait for a message
/*This works properly*/
DNM_temp = DNMManager_Ctor(DNM_temp,
&pl_p->Data[NAME],
pl_p->Addr,
*(u32*)&pl_p->Data[SN],
*(u32*)&pl_p->Data[SUBMASK]);
/*following code is omitted*/
}
现在,除了Payload_Manager.c中的函数外,我不希望其他文件知道类型“ ATEIS_Payload_s”。因为只有Payload_Manager.c中的函数才处理类型“ payload”。
换句话说,我想将CmdHndlr.c中的代码更改为:
//#include "Payload_Manager.h" /*no need anymore*/
#include "DNM_Manager.h"
int main(void){
void* pl_p = NULL; //programmer no need to know what type pl_p is
void* DNM_temp = NULL;
pl_p = OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msgSize, &ts, &err); //wait for a message
DNM_temp = DNMManager_Ctor(DNM_temp, pl_p); //DNM_Manager_Ctor will deal with it.
/*following code is omitted*/
}
这里值得一提:函数“ DNM_Manager_Ctor”同时处理类型“ DNM”和“ ATEIS_Payload_s”。
通过让DNM_Manager_Ctor知道“ DNM”和“ ATEIS_Payload_s”两个类型来实现。意味着将DNM_Manager.c更改为:
typedef struct
{
u32 Addr;
u32 SerialNo;
u32 SubnetMask;
char Name[NAME_SIZE];
}DNM;
typedef struct __attribute__((__packed__)) //oops, already declared in somewhere else. Is this valid?
{
u32 Addr;
u16 Cmd;
u16 Len;
u8 Data[0];
}ATEIS_Payload_s; //payload
void* DNMManager_Ctor(void* _this,
void* _dest)
{
DNM* this = (DNM*)_this;
ATEIS_Payload_s* dest = (ATEIS_Payload_s*)_dest;
/*following is omitted*/
return result;
}
我不知道这种方式是否有效。但是,即使有效,这种方式也会明显降低模块化性。
有没有更好的方法来解决它?
编辑:
我是面向对象的新手。尽管它很粗糙,但我正在尝试在C中实现此概念。另外,我很可能在此程序中滥用了这个概念。
答案 0 :(得分:2)
您正在向错误的目标射击。
您的目标不应该是将每个指针变成void*
,
因为那样您就放弃了C编译器可以提供的所有(有限)类型安全。
相反,您的目标应该是向客户隐藏结构的定义(即内部结构)。
this文章中介绍了操作方法。
答案 1 :(得分:1)
您的示例很长,因此我将尽力帮助您了解信息隐藏和adts的概念。
在谈论信息时,要记住的一件事是,如果给外部用户提供h文件,那么他就可以使用c函数,而无需了解编码器是如何“实现”的,例如您的 Payload_Manager.h 用户不需要知道结构的构建方式。他从这个模块中唯一需要的就是它提供的功能。因此,编写此h文件的正确方法是,首先将结构移至c文件,然后仅将该结构的typedef放入h文件中:
struct __attribute__((__packed__)) ATEIS_Payload_s
{
u32 Addr;
u16 Cmd;
u16 Len;
u8 Data[0];
};
并在您的h文件中:
typedef struct ATEIS_Payload_s ATEIS_Payload_s;
我不确定为什么您的create函数返回一个(void *)。 考虑一下。如果用户使用 void * Payload_Manager_Ctor 功能,则他希望获取有效负载。 同样,您必须在h文件的c文件中声明所有用户预期的无静态函数。因此现在它们可以按外部用户的意愿使用。
简而言之,H文件只需要具有其必须具有的所有内容,即可使用这些功能,而无需了解其工作方式的秘诀。
现在,关于抽象数据类型,使用它们的想法是例如当唯一需要知道它们是什么的是用户时。
考虑一个简单的链表或向量。您可以列出一个唯一的数据类型为int的列表。但是此实现现在限制了代码的可用性。如果您创建一个节点,而不是为数据携带void *,则对于用户可以在该列表中放置的内容没有任何限制。他可以存储指向结构,数组和chars ext的指针。列表的功能保持不变,数据将保存在列表中。但是什么样的数据呢?用户需要哪种类型。由于用户知道他已存储的内容,因此他将知道如何正确提取它并进行操作。
我希望这会有所帮助,祝你好运!