DLL con due classi: dipendenze

di il
14 risposte

DLL con due classi: dipendenze

Buon pomeriggio a tutti!
Ho sviluppato un codice in linguaggio C++ in ambiente Visual Studio per il funzionamento di un prototipo Hardware.
Il mio Software è basato sull'utilizzo di due classi: una classe che mi gestisce la connessione del dispositivo via usb, una classe più esterna in cui gestisco alcuni parametri del dispositivo collegato alla porta usb.
La mia classe più esterna richiama metodi della classe che gestisce la connessione seriale.
A questo punto dovrei sviluppare una DLL per accantonare il codice C e mettermi a sviluppare applicazioni a più alto livello.
Il problema è che non trovo da nessuna parte esempi di creazione di DLL a due classi. Non riesco a comprendere come gestire le dipendenze!
Avreste qualche link o libro da consigliarmi per comprendere come procedere?
Ho provato a sviluppare la DLL usando come classe Madre la mia più esterna. Il problema è che all'interno di tale codice , anche includendo il .h della DLL della classe più interna, mi dà errore (non riconosce i metodi).
Grazie a tutti

14 Risposte

  • Re: DLL con due classi: dipendenze

    In pratica vuoi ottenere due DLL o sbaglio?
  • Re: DLL con due classi: dipendenze

    In realtà ne vorrei una unica formata da due classi. Il fatto è che non so se si possa fare o se debba io creare due DLL distinte per forza (mi scuso per l'ignoranza ma è la prima volta che mi cimento nella creazione di una DLL).
  • Re: DLL con due classi: dipendenze

    Puoi fare tutto in una unica DLL. Resta da definire se vuoi esportare ambedue le classi o una sola, il come esportare la classe o le classi (in caso debbano essere usate indipendentemente), se ti interessa fare in maniera di essere indipendente dal compilatore usato per la compilazione (in fondo le DLL servono a questo), se vuoi essere indipendente dalla CRT usata dal compilatore (dipendenza statica o dinamica), se le classi devono interagire all'interno della DLL e cosa importante se vuoi poter usare la DLL solo in C++ o anche in altri linguaggi (C in primis). In quest'ultimo caso, sei costretto a usare l'ABI del C per l'interfacciamento (il che non significa scrivere la DLL in C, ma solo le funzioni da esportare).
    Tra l'altro DLL a 32 o 6a bit? (cambiano alcuni dettagli).
  • Re: DLL con due classi: dipendenze

    Grazie mille per la risposta. Allora inizialmente vorrei creare la .DLL per poterla utilizzare su MatLab e l'architettura è a 64bit.
    La cosa che non riesco proprio a comprendere è: come faccio a far coesistere le due classi anche nella DLL?
    La mia classe principale (chiamiamola A) sfrutta metodi della classe B. Praticamente con la mia classe A converto dei valori letti dalla seriale tramite la class B e effettuo Delle elaborazioni ( ho sviluppato 2 classi proprio per rendere più semplice il codice e nascondere il più possibile la classe di gestione della seriale).
  • Re: DLL con due classi: dipendenze

    Non conosco Matlab, tuttavia se una DLL deve essere generale (interfacciabile con ogni linguaggio), le funzioni esportate devono avere interfaccia C.
    Il che consente una serie di semplificazioni nel progetto generale.
    Una DLL è semplicemente (giusto per farti capire), un exe con un linking diverso. Pertanto una volta creato il progetto per la DLL, puoi aggiungere unità di compilazione (i vari .cpp), con i relativi file header esattamente come faresti per un qualsiasi exe. Quando tutto è pronto, fai il build e invece del file exe hai un file .DLL e un file .lib (usato per usare la DLL in progetti che richiedono un link statico).

    In genere si scrive un mini applicativo per testare le varie unità di compilazione e fare il debugging, dato che una volta creata la DLL non è possibile farlo. Poi quando tutto è a posto, si crea la DLL e si prova nell'applicativo vero e proprio.

    Domani ti scrivo un mini esempio di come fare.

    P.S. Che versione di Visual Studio?
  • Re: DLL con due classi: dipendenze

    Grazie mille, inizio a capire qualcosa! Come visual studio uso il 2017. Per passare a matlab ho già visto come dovrei fare : creare un codice in "linguaggio intermedio" eseguibile tramite matlab sfruttando le potenzialità del compilatore c++.
    Tuttavia questa fase di creazione della DLL è ancora più a monte. Dovrei creare questa DLL affinché ,se qualcuno in futuro volesse utilizzare il mio dispositivo, non si dovrebbe preoccupare più di tutta la parte di gestione della connessione alla porta seriale (con tutti i problemi che ho dovuto risolvere).
    Il mio codice è impostato in modo che: alla pressione di un tasto, corrisponde una funzione della mia classe A. Nella classe A vi è la chiamata ad una funzione della classe B ( con il classico costruttore.). Il problema è che ,sono riuscito (credo) a creare la DLL per la mia Classe A( con .h e .cpp) ma,anche creando analogamente la Classe B (sempre nello stesso progetto) , i metodi di quest'ultima non vengono riconosciuti .
    Se necessario e non sono stato abbastanza chiaro più tardi posso anche postare un breve pezzo di codice
  • Re: DLL con due classi: dipendenze

    Ecco parte del codice:
    Classe A.h:
    namespace MyHaptic
    {
    	class HapticTable
    	{
    	public:
    
    		MySerial arduino; //Costruttore
    		
    		int frequency;
    		int amplitude;
    
    		static HAPTICDLL_API bool Init_Device();
    		}
    }
    Classe A.cpp:
    namespace MyHaptic {
    
    	bool HapticTable::Init_Device()
    	{
    		bool success;
    		
    		arduino.open(COM_NAME);
    	}
    }
    Classe B.h
    namespace SerialPort{ //MySerial
    
    	class MySerial
    	{
    	private:
    		HANDLE handler;
    		bool connected;
    		COMSTAT status;
    		DWORD errors;
    	public:
    		static SERIALPORT_API void open(const char *portName);
    	}
    }
    Classe B.cpp
    namespace SerialPort{
    
    void MySerial::open(const char *portName)
    {
    	printf(" Inizializzazione dei parametri di Arduino e della porta seriale ...\n");
    	printf(" (...Sto connettendo...) \n");
    	system("mode com4: baud=115200 parity=o data=8 stop=1 xon=off"); //Stringa per i dettagli di connessione
    
    	this->connected = false; //prosegue...
    	}
    }
    In Classe A.ccp mi è segnalato errore arduino.open
  • Re: DLL con due classi: dipendenze

    Per passare a matlab ho già visto come dovrei fare : creare un codice in "linguaggio intermedio" eseguibile tramite matlab sfruttando le potenzialità del compilatore c++.
    Da quel che ho letto in giro, questo è necessario se vuoi usare direttamente il sorgente .c o .cpp. Se hai una DLL è sufficiente caricarla e usare le funzioni. Vedi cosa ti è più comodo.
    In Classe A.ccp mi è segnalato errore arduino.open
    Potrebbe solo essere un problema di namespace (se non li hai resi impliciti).
    Prova con
    namespace MyHaptic
    {
       class HapticTable
       {
       public:
    
          SerialPort::MySerial arduino; //Costruttore
    ...
    
  • Re: DLL con due classi: dipendenze

    Sei grande!!Mi sono andati via molti errori con questa aggiunta...mi è rimasto solo l'errore quando faccio arduino.open o arduino.isConnect applicando metodi della classe B. Il codice errore è : " l'elemento a sinistra di '.open' deve avere una classe, struttura o unione "
  • Re: DLL con due classi: dipendenze

    Ci sono vari errori nel codice.
    1) I metodi statici hanno accesso solo a variabili o metodi statici. SerialPort::MySerial arduino NON è una variabile statica, per cui (namespace a parte) è normale che in Init_Device() tu abbia errore. E vale lo stesso per la classe MySerial.

    2) la signature delle funzioni è sbagliata. Dev'essere:
    
    bool HAPTICDLL_API Init_Device();
    
    3)
    
    bool HAPTICDLL_API Init_Device();
    
    deve restituire qualcosa, e tu non lo fai.
  • Re: DLL con due classi: dipendenze

    Grazie per gli aiuti...ho corretto come mi hai detto e effettivamente ho risolto tutti i problemi che avevo. Per il 3) ho solo tagliato il codice per non rendere troppo pesante il post con codice incomprensibile.
    Ti ringrazio davvero tanto, sei stato gentilissimo!
  • Re: DLL con due classi: dipendenze

    Allora per la DLL.
    Matlab supporta funzioni esportate con interfaccia C, per cui non ti aspettare di usare direttamente degli oggetti.
    I file coinvolti sono 3 .cpp (in un paio ho fatto delle leggere modifiche dato che non idea di cosa siano, anche se posso intuirlo.
    Crea il progetto della Dll e aggiungi questi file:
    dllinterface.cpp
    
    // File di interfaccia tra gli oggetti C++ e le funzioni C
    // della DLL. Le eccezioni sono intrappolate in blocchi try catch
    // ovunque l'uso dell'oggetto possa sollevarne qualcuna.
    
    // funzione di creazione di HapticTable.
    // per Matlab il tipo a 64 corrispondente a uintptr
    // dovrebbe essere uint64Ptr
    // vedasi http://matlab.izmiran.ru/help/techdoc/matlab_external/ch2_shar.html
    // sezione Data Conversion
    // Eventuali errori devono essere gestiti con codici numerici, che il
    // chiamante è tenuto a controllare. 
    
    //-----------------------------------------------------------------------
    // la funzione restituisce l'indirizzo dell'oggetto creato
    // o zero in caso di fallimento.
    // eventuali eccezioni devono essere confinate all'interno della 
    // funzione.
    // sono supportate solo eccezioni standard.
    extern "C" {
    
    	__declspec(dllexport) uintptr_t __stdcall HapticTable_create() {
    		uintptr_t res = 0;
    		try {
    			// In caso l'allocazione fallisca è sollevata una std::bad_alloc()
    			// che viene intercettata dal catch sottostante.
    			MyHaptic::HapticTable* obj = new MyHaptic::HapticTable();
    			// converte l'indirizzo in formato numerico.
    			res = reinterpret_cast<uintptr_t>(obj);
    		}
    		catch (const std::exception& e) {
    			e;
    			// non fa nulla
    		}
    		return res;
    	}
    	//-----------------------------------------------------------------
    	// Ogni funzione che agisce su un oggetto Haptic_table deve ricevere
    	// il valore numerico dell'oggetto come parametro, convertirlo a puntatore
    	// e usarlo. Se si riceve 0 occorre gestire la situazione.
    	//-----------------------------------------------------------------
    	/* Libera un oggetto HapticTable */
    	__declspec(dllexport) void __stdcall HapticTable_destroy(uintptr_t value) {
    		if (value == 0) return;
    		delete reinterpret_cast<MyHaptic::HapticTable*>(value);
    	}
    	//-----------------------------------------------------------------
    	/* Inizializza il device
    	Restituisce 1 se l'inizializzazione riesce, 0 se fallisce
    	eventuali eccezioni devono essere confinate all'interno della
    	funzione.
    	Sono supportate solo eccezioni standard.
    	*/
    	__declspec(dllexport) uint8_t __stdcall HapticTable_init(uintptr_t value) {
    		if (value == 0) return 0;
    		try {
    			MyHaptic::HapticTable* obj = reinterpret_cast<MyHaptic::HapticTable*>(value);
    			return obj->Init_Device() ? 1 : 0;
    		}
    		catch (const std::exception& e) {
    			e;
    			// non fa nulla
    		}
    		return 0;
    	}
    }
    
    usb.h
    
    #pragma once
    #include "serial.h"
    // non sapendo cosa sia HAPTICDLL_API
    // lo riduco a __stdcall (in merito all'esempio)
    #define HAPTICDLL_API __stdcall
    namespace MyHaptic {
    	class HapticTable {
    	public:
    
    		SerialPort::MySerial arduino; //Costruttore
    		int frequency;
    		int amplitude;
    		bool HAPTICDLL_API Init_Device();
    	};
    }
    
    usb.cpp
    
    #include "stdafx.h"
    #include "usb.h"
    #define COM_NAME "com1"
    namespace MyHaptic {
    	bool HapticTable::Init_Device()
    	{
    		arduino.open(COM_NAME);
    		return arduino.is_connected();
    	}
    }
    
    Fine parte 1.
  • Re: DLL con due classi: dipendenze

    Parte 2.
    serial.h
    
    #pragma once
    #include <windows.h>
    // non sapendo cosa sia SERIALPORT_API
    // lo riduco a __stdcall (in merito all'esempio)
    #define SERIALPORT_API __stdcall 
    namespace SerialPort { //MySerial
    
    	class MySerial {
    	private:
    		HANDLE handler;
    		bool connected;
    		COMSTAT status;
    		DWORD errors;
    	public:
    		void SERIALPORT_API open(const char *portName);
    		bool is_connected() { return connected; }
    	};
    }
    
    serial.cpp
    
    #include "stdafx.h"
    #include "serial.h"
    #include <iostream>
    #include <fstream>
    namespace SerialPort {
    	void MySerial::open(const char *portName)
    	{
    		std::ofstream fp("log.txt"); 
    		if (fp) {
    			fp << " Inizializzazione dei parametri di Arduino e della porta seriale ...\n";
    			fp << " (...Sto connettendo...) \n";
    			fp << "mode com4: baud=115200 parity=o data=8 stop=1 xon=off" << std::endl; //Stringa per i dettagli di connessione
    		}
    		this->connected = true; //prosegue...
    	}
    }
    
    Una volta compilata la DLL (modalità release x64) crea un nuovo progetto di prova, prendi il file .dll e il file .lib e li copi nella cartella.
    progetto di prova aggiungi in source files il .lib e nel main scrivi:
    
    #include "stdafx.h"
    #include <cstdint>
    // prototipi delle funzioni esportate
    extern "C" __declspec(dllimport) uintptr_t __stdcall HapticTable_create();
    extern "C" __declspec(dllimport) void __stdcall HapticTable_destroy(uintptr_t);
    extern "C" __declspec(dllimport) uint8_t __stdcall HapticTable_init(uintptr_t value);
    
    int main() {
    
    	uintptr_t obj = HapticTable_create();
    	uint8_t res = HapticTable_init(obj);
    	HapticTable_destroy(obj);
        return 0;
    }
    
    eseguito l'applicativo di prova dovresti trovare un file di nome log.txt nella directory di progetto, segno che tutto è ok.
    Come vedi sono due classi in file diversi, all'interno della stessa DLL.
    L'unico file critico è dllinterface.cpp dato che è lui a doversi occupare degli aspetti di interfacciamento con ambienti esterni.
    A seconda che nella fase di creazione della DLL si scelga di utilizzare la CRT in formato DLL o statica, la DLL creata può essere usata ovunque senza runtime (link con CRT statica), oppure è necessario che nel sistema dove sarà usata la DLL sia presente la stessa CRT usata dal compilatore (link con CRT in formato DLL).
    Entrambi gli approcci sono validi ma hanno pro e contro. Valuta quale ti conviene usare.
    Ultima cosa.
    Matlab usa solo il caricamento dinamico della DLL, quindi il file .lib non gli serve ma può essere necessario per chi usa la tua DLL, nel qual caso devi anche creare un opportuno file header con i prototipi (come le prime righe del progetto di test)
    Qui documentazione su Matlab e DLL.
    http://matlab.izmiran.ru/help/techdoc/matlab_external/ch2_shar.html
  • Re: DLL con due classi: dipendenze

    Sei stato davvero gentile e ti ringrazio infinitamente! Questo pomeriggio lavorerò sul codice provando a modificarlo come mi hai suggerito. Grazie davvero!
Devi accedere o registrarti per scrivere nel forum
14 risposte