Reputation: 447
I learnt that ADT is an important concept and I'm learning this tech.
Here is a problem that I don't know how to deal with.
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*/
}
Now, I don't want else files to know type "ATEIS_Payload_s", except for functions in Payload_Manager.c. Because only functions in Payload_Manager.c deal with type "payload".
In other words, I want to change code in CmdHndlr.c to:
//#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*/
}
Here is a thing worthy noted: function "DNM_Manager_Ctor" deals with type "DNM" and "ATEIS_Payload_s" at the same time.
It's achieved by letting DNM_Manager_Ctor know both type "DNM" and "ATEIS_Payload_s". Meaning that changing DNM_Manager.c to:
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;
}
I don't know whether this way is valid. But this way will clearly decrease modularity, even it's valid.
Is there any better way to solve it?
edit:
I'm new to object oriented. I'm trying to implement this concept in C although it's very rough. Also, it's highly likely that I misused this concept in this program.
Upvotes: 0
Views: 102
Reputation: 517
Your example is very long, so I will try and help you get the notion of information hiding and adts.
when talking about Information hiding the thing to keep in mind is that if you give an outside user your h file, he would be able to use your c functions without the knowledge of how the coder "made it happen" for example in your Payload_Manager.h the user doesn't need to know how the struct is built. the only thing he needs from this modole are the functionalities it gives. So a proper way to write this h file would be first to move the struct to your c file and only place the typedef of that stuct in your h file:
struct __attribute__((__packed__)) ATEIS_Payload_s
{
u32 Addr;
u16 Cmd;
u16 Len;
u8 Data[0];
};
and in your h file :
typedef struct ATEIS_Payload_s ATEIS_Payload_s;
I'm not sure why your create function returns a (void*). think about it. if a user uses void* Payload_Manager_Ctor function- he wants to get payload. also, you must declare all the user intended none static function on your c file in the h file. so now they can be used as intended by an outside user.
In a nutshell, the H file need to have only the things that it MUST have in order to use the functionalities but not the recipe for how they do what they do.
Now about Abstract data types, the idea of using them is for instances when the only one who needs to know what they are is the user.
Think about a simple linked list, or a vector. you can make a list that the only type of data that is stores is int. but this implementation now restricts the usability of your code. if you make a node that instead carries a void* for the data, there are no restriction for the user on what can be placed in that list. he can store pointers to structs, arrays, chars ext. the functionality of the list stays the same, the data will be saved in a list. but what kind of data? whatever kind the user needs it to be. since the user knows what he had stored, he will know how to extract it correctly and manipulate it.
I hope this helps, good luck!!
Upvotes: 1
Reputation: 499
You're shooting at the wrong target.
Your objective shouldn't be to turn every pointer into void*
,
because in that way you're giving up all the (limited) type safety that the C compiler can provide.
Instead, your objective should be to hide the definition (that is, the internals) of the structs to the clients.
How to do so is explained in this article.
Upvotes: 2