0

Suppose I have a program.c that needs element_123 to do some operations, and element_123 can be accessed by including agent.h

/*program.c*/
#include "agent.h"

uint32_t element_123 = 0;
agent_get_element_123(&element_123);

uint32_t data = element_123 + some_data;

/*do operations...*/

element_123 may be implemented by different chips in different projects, and only one chip exists in a project.

/*chipA.c*/
#include "agent.h"

void chipA_get_element_123(uint32_t* output)
{
    //dummy code to get element_123 from chip A
}

agent_t agent = {&chipA_get_element_123};

/*chipB.c*/
#include "agent.h"

void chipB_get_element_123(uint32_t* output)
{
    //dummy code to get element_123 from chip B
}

agent_t agent = {&chipB_get_element_123};

My goal is to use agent.c to provide a unique interface to program.c. In other words, agent_get_element_123 is a compile-time polymorphic function that depends on chip.

/*agent.h*/

typedef struct
{
  void (*get_element_123)(uint32_t* output);
}agent_t;

void agent_get_element_123(uint32_t* output);

/*agent.c*/

extern agent_t agent;

void agent_get_element_123(uint32_t* output)
{
  agent.get_element_123(output);
}

My questions are:

  1. The minimum knowledge that program.c needs to know is agent_get_element_123, and having nothing to do with agent_t. However, chipA.c and chipB.c need to know it. Is there a way to decouple agent_t from program.c while keeping its relation with chipA.c and chipB.c?

  2. Does this technique deserve to be called a design pattern? If so, what is this design pattern called?

  3. This technique undoubtedly is just one of many approaches out there. What's are differences between this technique and other alternatives?

1 Answer 1

2

I'd call this compile-time polymorphism.

Architecturally this is a Platform Independence Layer, or a System Services Layer.

Design pattern wise, its a Plugin.


without agent_t:

/*agent.h*/

void agent_get_element_123(uint32_t* output);
/*chipA.c*/
#include "agent.h"

void agent_get_element_123(uint32_t* output)
{
    //dummy code to get element_123 from chip A
}
/*chipB.c*/
#include "agent.h"

void agent_get_element_123(uint32_t* output)
{
    //dummy code to get element_123 from chip B
}

And no need for agent.c

You may need to have an init() function to perform module level setup (and perhaps even a deinit() or equivalent for teardown).

5
  • A voice telling me that prefixing filename to function name increases code quality, and I always follow this rule. However; after reading this answer, I feel that a little offense may be a good choice sometime.
    – Andy Lin
    Mar 9, 2021 at 8:39
  • 1
    @AndyLin I don't see how that is anything more than Hungarian Notation applied to function names. I can see how it might be useful if a file represents a module, as that would be a form of namespace management encoding the module name into the function names for context. However here agent.h represents the agent module, and the two files chipA.c and chipB.c are implementations of that module. If it makes you happier call them agent.c in seperate folders \chipa\agent.c and \chipb\agent.c
    – Kain0_0
    Mar 9, 2021 at 8:46
  • I noticed a big difference between these two approaches. With my approach, program.c has no need to compile to adopt new chip, because program.c does not depend on source code of chips. Is it correct?
    – Andy Lin
    Mar 10, 2021 at 5:54
  • 1
    Why would program.c need to be recompiled? It depends solely on agent.h, not the implementations. Of course it will need to be relinked based on which implementation you choose.
    – Kain0_0
    Mar 10, 2021 at 6:09
  • I think your approach is better than mine in any aspect.
    – Andy Lin
    Mar 10, 2021 at 7:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.