C Bibliothek mit functionspointern mit c++ Klasse vereinen



  • Ich habe eine C-Bibliothek von NationalInstruments die eine von einer PCI Karte Daten ausliest. Dabei sorgt der Systemtreiber dafür das in regelmäßigen Abständen eine Funktion aufgerufen wird (aus der Bibliothek) die die Daten entgegennimmt.

    Das ganze sieht in C dann so aus (Ausschnitt):

    int32 CVICALLBACK EveryNCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData);
    int32 CVICALLBACK DoneCallback(TaskHandle taskHandle, int32 status, void *callbackData);
    
    int main(void)
    {
    	...
    	DAQmxRegisterEveryNSamplesEvent(taskHandle,
    		DAQmx_Val_Acquired_Into_Buffer,
    		1000,0,EveryNCallback,NULL);
    	DAQmxRegisterDoneEvent(taskHandle,0,DoneCallback,NULL);
    	DAQmxStartTask(taskHandle);
    	getchar();
    	return 0;
    }
    
    int32 CVICALLBACK EveryNCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData)
    {
    ...
    }
    
    int32 CVICALLBACK DoneCallback(TaskHandle taskHandle, int32 status, void *callbackData)
    {
    ...
    }
    

    Es werden also Functionspointer übergeben mit den Funktionen die später genutzt werden sollen.

    Das Format dieser Funktionen ist dabei eindeutig festgelegt als:

    typedef int32 (CVICALLBACK *DAQmxEveryNSamplesEventCallbackPtr)(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData);
    typedef int32 (CVICALLBACK *DAQmxDoneEventCallbackPtr)(TaskHandle taskHandle, int32 status, void *callbackData);
    

    wobei

    #define CVICALLBACK     __cdecl
    

    Jetzt habe ich aber eine Klassenstruktur und echten C++ Code und möchte dort das ganze integrieren. Ich möchte also aus solchen Funktionen heraus die Ergebnisdaten in der Klasse speichern können. Nur sehe ich nicht wie ich das realisieren sollte ohne das die Funktionen member der Klasse sind.

    Bin für jede Idee dankbar.

    Matthias



  • Das ist immer wieder das gleiche Schema F. Haben wir hier bestimmt auch schon mehrfach beschrieben.

    Du gibst nicht nur einen Funktionszeiger an, sondern kannst auch einen beliebigen void* übergeben, der von der API einfach weitergereicht wird.

    Beispiel:

    struct foo {
      int mehrdaten;
      int32 blah(TaskHandle, int32);
    };
    
    extern "C" {
       int32 CVICALLBACK mein_DoneCallback(
             TaskHandle taskHandle,
             int32 status,
             void *callbackData)
       {
          return static_cast<foo*>(callbackData)->blah(taskHandle,status);
       }
    }
    
    int main() {
       ...
       foo f;
       ...
       DAQmxRegisterDoneEvent(taskHandle,0,mein_DoneCallback,&f);
       ...                                                   ^^
    }
    


  • Wichtig dabei ist, dass man bei den Zeigerkonvertierungen aufpasst. Angenommen, X sei eine Basisklasse und Y davon abgeleitet, dann gilt:

    X* -------> void* -> X*  (OK)
    Y* -------> void* -> X*  (NICHT OK)
    Y* -> X* -> void* -> X*  (OK)
    


  • Dann sollte also das hier funktionieren?:

    extern "C" {
        int32 CVICALLBACK EveryNCallback(TaskHandle taskHandle,
                                         int32 everyNsamplesEventType,
                                         uInt32 nSamples,
                                         void *callbackData)
        {
            if (callbackData) {
                static_cast<Task*>(callbackData)->readData();
            }
            return 0;
        }
    }
    
    void Task::run()
    {
        DAQmxRegisterEveryNSamplesEvent(d->taskHandle,DAQmx_Val_Acquired_Into_Buffer,1000,0,EveryNCallback,(void *)this);
        //DAQmxRegisterDoneEvent(d->taskHandle,0,DoneCallback,(void *)this);
    
        DAQmxStartTask (d->taskHandle);
    }
    
    void Task::readData()
    {
    ...
    }
    

    kompiliert auf jeden Fall.


Log in to reply