2

I have several hardware signals that get toggled based on properties relevant to scenarios in which respective signals could be toggled. The problem is that signals and the properties that define scenarios, all three could change. I am forced to think in terms of a modular framework based design in which there is SignalManager that handles signal creation and there is a SignalPropertiesData with its SignalPropertiesDataManager that associate certain SignalScenario structure and all this is created specifically for any type of signal by the SignalManager. I wish to follow the public interface, private data in the C programming paradigm.

My dilemma is C in general when it comes to type safety and this kind of problem, the only solution is to lose type safety and use 'void' for any and all types of data. Can you point me to any code or component in the vast opensource sea, which can serve as a right reference for this problem.

signal_manager.h:

#ifdef _SIGNAL_MANAGER_H
#define _SIGNAL_MANAGER_H

int createSignal(SignalDescPtr signalDescPtr);

int destroySignal();

typedef struct SignalDesc* SignalDescPtr;

#endif

signal_manager.c:

#include "signal_manager.h"

typedef struct {
  char* signalName;
  unsigned int signalId;
  SignalPropertiesDataPtr signalProperties;
} SignalDesc;

signal_properties_data.h:

#ifdef _SIGNAL_PROPERTIES_DATA
#define _SIGNAL_PROPERTIES_DATA

typedef enum {
  SIGNAL_DATA_INT_TYPE,
  SIGNAL_DATA_UNSIGNED_INT_TYPE,
  SIGNAL_DATA_FLOAT_TYPE,
     :
  SIGNAL_DATA_UNSPECIFIED_BASIC_TYPE
} eSignalBasicType;

typedef enum {
  SIGNAL_DATA_LIST_ARRAY_TYPE,
  SIGNAL_DATA_LIST_ADT_TYPE,
   :
   :
  SIGNAL_DATA_LIST_UNSPECIFIED_TYPE
} eSignalComplexType

typdef  union {
  eSignalBasicType signalBasicType;
  eSignalComplexType signalComplexType;
} eSignalType;

typedef struct {
  eSignalType signalType;
  unsigned int signalDataLen;
} SignalDataValueType;    

typedef SignalPropertiesData* SignalPropertiesDataPtr;

result_t setSignalType(..);
result_ getSignalType(..);
result_t setSignalData(..);
result_t getSignalData(..);
result_t setSignalDataLen(..);
result_t getSignalDataLen(..);

#endif

signal_properties_data.c:

#include "signal_properties_data.h"

typdef struct {
  SignalDataValueType signalPropertiesDataType;
  void* signalPropertiesDataValue;
} SignalPropertiesData;

signal_properties_data_mgr.h:

#ifdef _SIGNAL_PROPERTIES_DATA_MGR_H
#define _SIGNAL_PROPERTIES_DATA_MGR_H

#include "signal_properties_data.h"
#include "signal_scenario.h"

typedef SignalScenarioDesc* SignalScenarioDescPtr;

result_t createSignalPropertiesData(SignalPropertiesDataPtr *signalPropDataPtr, eSignalType desiredSignalType);

result_t freeSignalPropertiesData(..);

result_t associateSignalToggleScenario(SignalPropertiesDataPtr *signalPropDataPtr, SignalScenPtr signalScenPtr);

result_t disassociateSignalToggleScenario(SignalPropertiesDataPtr *signalPropDataPtr, SignalScenarioDescPtr signalScenPtr);

#endif 

signal_properties_data_mgr.c:

#include "signal_properties_data_mgr.h"

typedef struct {
  toggleFuncPtr fptr;
} SignalScenarioDesc;
Emil
  • 7,220
  • 17
  • 76
  • 135
  • "...I wish to follow the `public` interface, `private` data in the C programming paradigm..."... I am looking forward to explanation for this one.... – Recker Oct 21 '12 at 00:59
  • See part 1 of "Patterns in C" by Adam Petersen. Search in Google. Its similar to the declaration of a first class ADT in C. So one defines a pointer to an incomplete type in the header file and the definiton is hidden in the C file with implementation for public functions that manipulate this data. So the user is now forced to use only public functions. Another way of doing this is to encapsulate the incomplete type in another structure with the same public functions exposed as function pointers ... The second structure is exposed to the public in the .h file – Manish Kochhal Oct 21 '12 at 01:03

2 Answers2

0

Avoid going for void *. It loses the benefits of prototypes and is not necessary,

Since this is C, you should write in signalmanager.h

#ifndef SIGNAL_MANAGER_H_INCLUDED
#define SIGNAL_MANAGER_H_INCLUDED

typedef struct SignalDesc* SignalDescPtr;

int createSignal(SignalDescPtr signalDescPtr);

int destroySignal(void);

#endif

Changes:

  1. Critical: the idiom is #ifndef MACRO / #define MACRO / #endif. You used #ifdef which won't work.
  2. Place typedef before it is used.
  3. Add explicit (void) to make destroySignal(void) into a prototype. Your version simply says 'there is a function destroySignal() that returns an int but it takes an unspecified (but not variadic) argument list'.
  4. Do not use reserved name space (leading underscore, capital letter) for the header protection guard.

I prefer not to hide pointers in data type typedefs, so I'd probably write:

#ifndef SIGNAL_MANAGER_H_INCLUDED
#define SIGNAL_MANAGER_H_INCLUDED

typedef struct SignalDesc SignalDesc;

extern int createSignal(SignalDesc *sigdesc);

extern int destroySignal(void);

#endif /* SIGNAL_MANAGER_H_INCLUDED */

I'm not sure that the interfaces to the create and destroy are correct, but that's another subject for discussion. I'd normally expect to find that the interfaces are more like:

extern SignalDesc *createSignal(const char *name, int signum);
extern void destroySignal(SignalDesc *sigdesc);

The implementation file signal_manager.c would then define the structure type and use it; the external interface is through functions that work with pointers.

#include "signal_manager.h"
#include "signal_properties_data.h"

struct SignalDesc
{
    char                 *signalName;
    unsigned int          signalId;
    SignalPropertiesData *signalProperties;
};

The signal_properties_data.h needs similar cleaning up:

#ifndef SIGNAL_PROPERTIES_DATA_H_INCLUDED
#define SIGNAL_PROPERTIES_DATA_H_INCLUDED

typedef enum {
  SIGNAL_DATA_INT_TYPE,
  SIGNAL_DATA_UNSIGNED_INT_TYPE,
  SIGNAL_DATA_FLOAT_TYPE,
     :
  SIGNAL_DATA_UNSPECIFIED_BASIC_TYPE
} eSignalBasicType;

typedef enum {
  SIGNAL_DATA_LIST_ARRAY_TYPE,
  SIGNAL_DATA_LIST_ADT_TYPE,
   :
   :
  SIGNAL_DATA_LIST_UNSPECIFIED_TYPE
} eSignalComplexType

typdef  union {
  eSignalBasicType signalBasicType;
  eSignalComplexType signalComplexType;
} eSignalType;

typedef struct {
  eSignalType signalType;
  unsigned int signalDataLen;
} SignalDataValueType;

/* Huge hole in types here - or is SignalValueDataType what you're after? */
typedef struct SignalPropertiesData SignalPropertiesData;

result_t setSignalType(..);
result_t getSignalType(..);
result_t setSignalData(..);
result_t getSignalData(..);
result_t setSignalDataLen(..);
result_t getSignalDataLen(..);

#endif  /* SIGNAL_PROPERTIES_DATA_H_INCLUDED */

This revised header is not self-contained yet. It does not define result_t, so it should include the header that does. I assume the ... notation in the function calls is "do not want to specify in this question" because it isn't valid in C; you must have one argument of known type before you specify the ellipsis.

It is not clear from the functions in the header why the user of the header needs to know about the types that are defined in it. Think of a header as a resource that will be shared. It 'belongs to' or describes the external interface to a file (or a small set of files). It gives the users the minimum data that they need to use the facilities provided by the code, but no more than the minimum. You sometimes find that a set of files implementing a facility will need a private header to share between themselves as well as the public header that other code ('customer' code or 'consumer' code) uses.

The key to opaque types is that the consumer doesn't need to know the internal details of a structure to be able to use pointers to the structure type (see Which part of the C Standard allows this code to compile?, Does the C standard consider that there are one or two struct uperms_entry types in this header? and How to structure header files in C for some more insight).

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Jonathan, thanks for your answer. My background has been very weird Java --> C++ --> C, so I find my skills to translate a good structured design into C (with application in embedded real time systems) very limited. – Manish Kochhal Oct 21 '12 at 01:28
  • I think you are right, I should maintain a private header file and may be a customer specific header file with only the pointer to the structure (I dont know why you think a pointer to an incomplete type is not a good idea). – Manish Kochhal Oct 21 '12 at 01:33
  • Regarding hiding the enums for types, I think the internal framework code i.e. the private modules that setup the framework would have no clue as to how they could go up design signals that are based on different types of signal properties data. The last portion of the code, where there is a definition of a function pointer within the SignalScenDesc, I wished to point to a customer specific function that determines the thresholds when the signal should toggle. So in essence, the data is maintained by the framework but the client provides the toggle function. – Manish Kochhal Oct 21 '12 at 01:34
  • I don't see the function pointer you're referring to. The technique is to define the interface to the callback function in terms that the user knows, and then the user provides a pointer to a function that meets the interface. For example, `typedef int (*SignalThreshold)(SignalFuncDesc *desc, int curr_level);`. You provide a configuring function that takes a `SignalThreshold` (pointer to function — I do hide pointers to functions) and keeps it somewhere in your internal data structures for calling back. The user passes a pointer to their `int sig_thresh(SignalFuncDesc *desc, int curr_level)`. – Jonathan Leffler Oct 21 '12 at 01:45
  • Thanks Jonathan, I will try to put this in and I think hopefully it will conform to different signal types and it works. Many thanks for your help. – Manish Kochhal Oct 21 '12 at 02:14
  • how would my struct SignalPropertiesData look like? Can I still avoid the void* signalPropertiesDataValue as I earlier had in typdef struct { SignalDataValueType signalPropertiesDataType; void* signalPropertiesDataValue; } SignalPropertiesData; – Manish Kochhal Oct 21 '12 at 02:31
  • I'm not clear how you're going to tell which of `eSignalBasicType` and `eSignalComplexType` is in the SignalValueDataType. I'm also not clear what type you'd like the `void *` to be; maybe a `SignalPropertiesDataValue` given the element name...but I can't find that type in the maze of types and headers with mile-long names. I'm not surprised to hear you come from a Java background; C programmers use spartan (short) names. It wasn't so long ago that 8.3 limits on file names were real. They aren't any more, but C programmers have not completely adapted. – Jonathan Leffler Oct 21 '12 at 02:45
  • There is a union for that type which is one of basic or complex. The void* has to be typecasted based on this above type that the user specifies. Hence the need to expose enums for these types in the header file. May be I need another union to map the enum type to a real type. I got this from this link: http://stackoverflow.com/questions/5551427/generic-data-type-in-c-void?rq=1 I think this logic of having a union tie in a user defined type to real type may help. But then it may not work for the complex ADT types. – Manish Kochhal Oct 21 '12 at 02:58
  • regarding mile long names, you got it right, courtesy Java :-) – Manish Kochhal Oct 21 '12 at 03:00
  • One header includes `signal_scenario.h`; you didn't provide `signal_scenario.h`, which complicates life. – Jonathan Leffler Oct 21 '12 at 03:16
0

I am not sure that I fully understand what your needs are, but I think that you could look at WIN32 handles for a reference implementation.

A short example would be to define a macro that lets you define custom handles like this:

/* example.h */

#ifndef _EXAMPLE_H
#define _EXAMPLE_H

/* Define macro */
#define DECLARE_HANDLE(HandleName) typedef struct HandleName##Tag * HandleName

/* Declare some handles */
DECLARE_HANDLE(SomeHandle);
DECLARE_HANDLE(SomeOtherHandle);

#endif /* _EXAMPLE_H */

/* example.c */

#include "example.h"

struct SomeHandleTag {
    int foo;
};

struct SomeOtherHandleTag {
    int foo;
};
Community
  • 1
  • 1
npclaudiu
  • 2,401
  • 1
  • 18
  • 19