Come gestire le enumerazioni in un punto di riferimento indiretto con functionpointer ANSI-C?

1

Moinsen,

Sono in qualche modo bloccato in un problema di progettazione. La lingua è ANSI-C.

Supponiamo di avere un congegno di moduli software:

  • un modulo per la logica Logica
  • (almeno) un modulo che esegue alcuni logging Logger
  • due moduli, entrambi che danno una "cornice" per far funzionare il programma, diciamo
    • uno con una GUI
    • uno per linea di comando
  • ...

Pertanto, la stessa logica potrebbe essere utilizzata in un comando e in una versione grafica del software. Logica deve registrare alcuni errori ma non dovrebbe sapere nulla del logger specifico in quanto potrebbe dipendere dal "frame". È ovvio fornire a Logica un puntatore a funzione che deve essere riempito dal fotogramma per associare il Logger utilizzato alla Logica .

Al Logging -Module (tutto il codice Pseudo-ANSI-C):

    void Logger_Log(char *sLogText)
    {
      //do some stuff
    }

In Logica -Module:

    void Logic_PseudoLog(char *sLogText)
    {
      printf(sLogText);
    }

    void(* Logic_Log)(char *sLogText) = &Logic_PseudoLog;

    void Logic_SetLogger(void(* LogFct)(char *sLogText))
    {
      Logic_Log = LogFct;
    }

In GUI / Cmd-Line:

    #include "Logger.h"
    #include "Logic.h"
    Logic_SetLogger(&Logger_Log);

Ora desidero introdurre diversi livelli di gravità per la registrazione e implementarli come enum in Logger :

    //Logger.h:
    typedef enum {
      DEBUG,
      INFO,
      ERROR
    } teLogLevel;

    void Logger-Log(char *sLogText, teLogLevel eLevel);

E qui sorge il problema: il puntatore alla funzione di Logic deve avere la firma corretta. Per fare ciò, deve conoscere teLogLevel . Pertanto Logica deve conoscere il Logger , esattamente il caso che volevo evitare con l'indiretto in primo luogo.

    #include "Logger.h"
    void(* Logic_Log)(char *sLogText, teLogLevel eLevel);

La situazione presentata è solo un esempio. Per favore non risolverlo dicendo qualcosa come "usa int invece di enum " o "crea tre funzioni per i livelli". La domanda di bottemline è:

Come gestire le enumerazioni in un punto di riferimento indiretto con functionpointer in ANSI-C?

Come "iniettare" enum in un modulo, che non dovrebbe ora sull'origine delle enumerazioni?

    
posta Oliver 02.07.2014 - 09:03
fonte

2 risposte

1

Nel tuo progetto attuale, il modulo Logica ha già una dipendenza dal modulo Logger , ma tale dipendenza è nascosta. La dipendenza sta nel fatto che sia Logic che Logger devono essere d'accordo sul set di funzioni utilizzate per la registrazione (una funzione) e le loro firme ( void (char*) ).

La via d'uscita è separare nettamente l'interfaccia dall'implementazione . In particolare, il Logger modulo solo fornisce l'interfaccia di registrazione. Le implementazioni sono fornite dai moduli GUI / Cmd-Line o da moduli separati di Logger-Implementazione che possono dipendere dai moduli GUI / Cmd-Line.

Il modulo Logger consiste solo in un singolo file di intestazione, come questo

/* File: Logger.h */
typedef enum {
  DEBUG,
  INFO,
  ERROR
} teLogLevel;

typedef void (*LoggerFuncT)(char *sLogText, teLogLevel eLevel);

/* Logger function that discards all logging. Can be used as a safe default. */
static void NullLogFunc(char*, teLogLevel) {}

Tutti gli altri moduli che utilizzano o implementano la funzionalità di registrazione devono fare riferimento a questa intestazione per conoscere le firme delle funzioni corrette e altri tipi (incluse le enumerazioni).
Ad esempio, il modulo Logica lo userebbe in questo modo:

#include "Logger.h"
LoggerFuncT Logic_Log = NullPseudoLog;

void Logic_SetLogger(LoggerFuncT LogFct)
{
  Logic_Log = LogFct;
}

void Logic_DoSomething()
{
  Logic_Log("Entering Logic_DoSomething()", DEBUG);
  //...
  Logic_Log("Leaving Logic_DoSomething()", DEBUG);
}

Nel modulo GUI, potresti avere un'implementazione come questa:

void GUI_LogToDebugWindow(char* sText, teLogLevel eLevel)
{
    //...
}

/* Let the Logic module use this function as logger.
 * The compiler will complain here if the logger signatures don't match. */
Logic_SetLogger(GUI_LogToDebugWindow); 
    
risposta data 02.07.2014 - 10:57
fonte
0

Logic.h dichiara una funzione per impostare la funzione logger: Logic_SetLogger() , per poterlo utilizzare.

Pertanto, Logic.h deve conoscere il prototipo della funzione logger, qualunque sia il Logger.h , cioè nel modo più generico possibile.

Questo è già il caso del primo argomento: un puntatore a un carattere.

Per il secondo argomento, vedo due alternative:

  1. Il solito modo di gestirlo è usare un int, che è la forma generica di un enum. Rappresentare un livello di errore con un int è molto comune tra gli sviluppatori di C.

  2. Nel tuo caso, non volendo un int, devi usare un tipo enum nella definizione prototipo della funzione logger (che è in Logic.h come argomento di Logic_SetLogger() ). Pertanto, Logic.h deve anche definire il tipo enum.

Secondo me, definire i livelli di errore in Logic.h non è una buona scelta di design.

    
risposta data 02.07.2014 - 09:46
fonte

Leggi altre domande sui tag