strato anti-corruzione Blue Giga C ++ per l'API callback C?

0

Sto lavorando con un dongle USB Blue Giga BLE112. Forniscono un'API sotto forma di diverse intestazioni e amp; * File .c. Funziona tutto fuori dai callback. Ad esempio.

int main()
{
    // defined in a "cmd_def.h" & implemented in "cmd_def.c"
    ble_cmd_connection_get_status();

    // read messages off the usb serial port forever
    // At some point, we'll read the message that triggers the connection status event.
    while (true) { readMessages(); }
}

// Call back that we implement
void ble_evt_connection_status(const struct ble_msg_connection_status_evt_t *msg)
{
    // This is terrible because we could call the command from anywhere 
    // and have no idea what context the event is firing under 
    // with no way to pass any information into the callback except from global state.

    if (msg->flags & connection_connected) 
    {
        printf("WE'RE CONNECTED!);
    }
}

Ci sono circa 100 coppie di questi Command se responseEventCallback s.

Ora, sto cercando di implementare una piccola applicazione per console C ++, ma ne sto facendo un uso estremamente difficile. Ho cercato di avvolgere questo comportamento in una classe C ++, ma sto fallendo miseramente. Ad ogni turno mi imbatto in problemi cercando di utilizzare questi callback per impostare lo stato in classe. (Ovviamente, non posso perché i callback sono statici e non posso accedere ai membri della classe.)

Era un'idea sbagliata? Ho pensato che se avessi potuto introdurre uno strato anti-corruzione OO, avrei potuto rendere più facile occuparmi dello stato globale e dell'inferno del callback in cui mi sono trovato.

Qualcuno ha qualche idea su come implementare in modo pulito questo tipo di API callback C in C ++? Sto guardando tutti questi richiami pensando che questo sarà un incubo da mantenere, ma non sono in grado di capire come avvolgere questi eventi di comando / risposta in una classe.

Sarebbe meglio semplicemente dimenticare le classi e implementarle come un "modulo" di funzioni?

Per riferimento, ecco un esempio di ciò che sto cercando di realizzare.

// nice clean OO api

bool scanning = false;

BLUEGIGA_API bool BlueGiga::IsConnected()
{
    _isConnected = false;

    ble_cmd_connection_get_status(0);
    scanning = true;

    while (scanning)
    {
        if (!ReadMessage())
        {
            return false;
        }
    }

    return _isConnected;
}

// C callback that should not be part of my C++ api, nor can it be a member because C can't use C++ members as callbacks.

void ble_evt_connection_status(const struct ble_msg_connection_status_evt_t *msg)
{
    // _isConnected isn't identified because we don't have a 'this' instance in here.
    _isConnected = (msg->flags & connection_connected);

    scanning = false;
}
    
posta RubberDuck 24.08.2016 - 14:56
fonte

2 risposte

1

Fondamentalmente non stai solo portando l'API in un modo orientato agli oggetti, ma vuoi trasformarla in un'API sincrona da un'API asincrona?

Non mi sembra una buona idea se sia stato fatto in questo modo, potrebbe esserci qualche motivo mentre non dovresti cambiarlo.

Le API C ++ con callback non sono nulla di invisibile e nulla di incompatibile con OOP, puoi trovarle nella libreria di rete asincrona boost (Boost ASIO: link ), potrebbe sembrare un po 'interessante. Vi darà un'idea.

Nota: leggere le intestazioni di boost potrebbe essere un po 'difficile.

Alla fine penso che dovresti mantenere il modo asincrono in OOP. Questo è sicuramente fattibile usando una sintassi non molto obiettiva, ma non so quale.

E per il design della tua applicazione, pensa a come la UI funziona in generale, di solito hai un thread per l'interfaccia utente, uno (o più) per il resto, credo che sia il modo migliore per farlo. Il "thread UI" bloccherà ogni nuovo comando mentre quello attuale viene elaborato, quindi stamperà il risultato e chiederà un nuovo comando. Il thread UI sarà richiamato dall'API in esecuzione nell'altro.

    
risposta data 24.08.2016 - 15:24
fonte
0

Finché si eseguirà solo uno di ciascun tipo di richiesta alla volta, è possibile mantenere una mappa della funzione di callback su questo puntatore. Penso che questo sia il tuo caso d'uso perché stai eseguendo il ciclo su ReadMessage all'interno di ogni funzione di comando. Forse qualcosa di simile a questo:

#include <stdio.h>
#include <map>

// provided by the library - somehow

const int connection_connected = 0x01;
struct ble_msg_connection_status_evt_t { int flags; } msg = {0xFF};
void ble_evt_connection_status(const struct ble_msg_connection_status_evt_t *msg);
void ble_cmd_connection_get_status(int) { }
bool ReadMessage() { ble_evt_connection_status(&msg); return true; }

// provided by you

struct BlueGiga;

std::map<void*,BlueGiga*> this_finder; // this could be in thread local storage...

struct BlueGiga
{
    bool IsConnected()
    {
        _isConnected = false;

        this_finder.insert(std::make_pair((void*)::ble_evt_connection_status,this));

        ble_cmd_connection_get_status(0);

        _scanning = true;

        while (_scanning)
        {
            if (!ReadMessage())
            {
                return false;
            }
        }

        return _isConnected;
    }

    void ble_evt_connection_status(const struct ble_msg_connection_status_evt_t *msg)
    {
        _isConnected = (msg->flags & connection_connected);
        _scanning = false;
    }

    int _isConnected;
    bool _scanning;
};

int main()
{
    BlueGiga blueGiga;
    return blueGiga.IsConnected();
}

// one for every callback - could use a macro to make it easier

void ble_evt_connection_status(const struct ble_msg_connection_status_evt_t *msg)
{
    BlueGiga* object = this_finder[(void*)::ble_evt_connection_status];
    object->ble_evt_connection_status(msg);
}

/* sample macro

#define DECLARE_CALLBACK(func,param) \
void func(const struct param *msg) \
{ \
    BlueGiga* object = this_finder[(void*)::func]; \
    object->func(msg); \
}

DECLARE_CALLBACK(ble_evt_connection_status,ble_msg_connection_status_evt_t)

*/
    
risposta data 03.11.2016 - 06:07
fonte

Leggi altre domande sui tag