Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 13
  1. #1
    Utente di HTML.it
    Registrato dal
    Jun 2003
    Messaggi
    4,826

    [c++]problema dll e factory

    ciao.
    ho un sottoprogramma che viene compilato e genera una dll.
    E pero' un progetto differente(risiede in un altra cartella) che include una parte delle classi del programma principale.

    IL problema è che quando includo un qualsiasi file .h nel sottoprogramma(quello che crea la dll)e compilo mi genera sempre l'errore del linker , perchè non trova il corpo della funzione che stà nel programma principale penso.

    Mi chiedevo se fosse possibile aggirare questo ostacolo facendosi passare per es. un puntatore a una funzione che crea l'oggetto.

    Il problema pero' secondo me è che non trova lo stesso il corpo della funzione.
    E' possibile creare in un qualche modo una factory nel programma principale che ritorna un istanza delle(poche) classi che mi servono senza includere il "mondo"?

    chiedo venia ma non ho ben chiaro come sia la dinamica delle cose con una dll che interagisce con un exe(il programma principale)


    grazie.

    es. errore:
    CmpImportApi error LNK2019: unresolved external symbol "public: __thiscall Factory1::Factory1(void)" (??0Factory1@@QAE@XZ) referenced in function "public: void __thiscall CCmpImportApi::Init(int)" (?Init@CCmpImportApi@@QAEXH@Z)

  2. #2
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  3. #3
    Utente di HTML.it
    Registrato dal
    Jun 2003
    Messaggi
    4,826
    grazie Shodan.
    Ho letto un po l'articolo devo capirlo pero'.
    Adesso quello che non capisco è questo :
    nell applicazione chiamiamola base(quella che ha un grande numero di include e file)ho un singleton e una volta istanziato se lo richiamo dall' applicazione che crea la dll mi trovo che l'istanza è ancora null.
    Quindi noncapisco.
    Perchè 2 istanze diverse?

    a dire il vero il casino mi sembra un altro:
    factory1.cpp
    codice:
    #include "stdafx.h"
    #include "factory1.h"
    
    using namespace std;
    Factory1* Factory1::instance_ptr;
    Factory1::Factory1(void)
    {
    
    }
    //
    Factory1::~Factory1(void)
    {
    }
    
    CBeamDSChess* Factory1::CreateDS()
    {
    	return (m_pMapRegister)[0]();
    }
    factory1.h
    codice:
    #pragma once
    #include "BeamDsChess.h"
    #include <map>
    typedef CBeamDSChess* (*BASE_CREATE_FN)();
    
    class Factory1
    {
    public:
    	Factory1(void);
    	~Factory1(void);
    	static Factory1* instance_ptr ;
    	static Factory1* get_instance() {
                    if (instance_ptr == NULL) {
                            instance_ptr = new Factory1();
                    }
                    return instance_ptr;
            }
    	CBeamDSChess * CreateDS();
    	std::map<int, BASE_CREATE_FN>  m_pMapRegister;
    
    private:
    };
    
    class registerInFactory
    {
    public:
        static CBeamDSChess* createInstance()
        {
    		AfxMessageBox("pippO");
    		CBeamDSChess * pDsChess = new CBeamDSChess();
    		return pDsChess;
        }
    	
        registerInFactory(BASE_CREATE_FN func)
        {
    	Factory1::get_instance()->m_pMapRegister[0] = func;
        }
    };
    nel file che creea la dll includo (e qui mi sembra di aver commesso un errore) il file .h(giusto) e anche il file.cpp(Factory1.cpp sbagliato) perchè altrimenti mi da un errore di linker quando includo solo il .h.

    solo che secondo me all inclusione del file .cpp questa istruzione mi resetta l'istanza:
    codice:
    Factory1* Factory1::instance_ptr = 0;
    Come posso condividere l'istanza del singleton tra applicazione base e applicazione che crea la dll?
    Nell articolo è spiegata una soluzione a questa problematica?

    penso possa essere un problema risolvibile col GetProcAddress per avere la stessa istanza dell'exe nella dll ma non so bene come.
    grazie.

  4. #4
    Utente di HTML.it
    Registrato dal
    Jun 2003
    Messaggi
    4,826
    ho provato a fare:
    codice:
            FNPTR pFn;
    	HMODULE mod=GetModuleHandle("C:\\testdata\\Frg\\Debug\\crtx.exe");
    
    	pFn=(FNPTR) GetProcAddress(mod, "FactoryDS");
    	
    	Factory1* F;
    	pFn(F);
    	CBeamDSChess* pChess = F->CreateDS();
    	int n = pChess->m_nChessBoard;
    	
    //////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////
    void _declspec(dllexport)  FactoryDS(Factory1*& pF)
    {
    	Factory1* pf = Factory1::get_instance();
    	pF = pf;
    }
    esportando l'istanza del singleton tramite la funzione FactoryDS e andando a pescare l'istanza con GetProcAddress.
    Il problema adesso è l'inclusione dei files , vorrei poter includere tutti gli header di cui ho bisogno nel programma principale e non in quello dove prendo il modulo e l'indirizzo dell'istanza del singleton.
    è possibile?
    altrimenti il problema è che tantissime funzioni di pChess-> richiedono header che nel progetto principale sono gia inclusi , ma non nel progetto che crea la dll , e non vorrei appesantire troppo quest ultimo , altrimenti non ha senso dividere i due progetti.
    grazie.

  5. #5
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Da quel che ho capito stai facendo proprio il contrario di quello che dovresti fare. E' la DLL che deve contenere il codice che devi condividere, non l'applicativo; per cui sarà la DLL ad avere il codice di Factory e di tutte le classi che ti servono per l'applicazione, non il contrario.
    Il codice sotto è un esempio. L'unica cosa che conoscerà la tua applicazione saranno le varie interfacce raccolte in uno o più file .h più una unica funzione di caricamento della Factory vera e propria.
    codice:
    #pragma once
    // #include "BeamDsChess.h"
    // #include <map>
    
    class CBeamDSChess; // classe interfaccia.
    class Factory1
    {
    public:
        virtual CBeamDSChess * CreateDS( int idOfObjectToCreate )=0;
    };
    
    extern "C" {
    void __declspec(dllexport)  Factory1* FactoryDS();
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Factory1.cpp
    ////////////////////////////////////////////////////////////////////////////////
    
    typedef CBeamDSChess* (*BASE_CREATE_FN)();
    
    class CBeamDSChess_type1 : public CBeamDSChess
    {
       public:
          static CBeamDSChess* instance() { return new CBeamDSChess_type1; }
          /* funzioni virtuali di CBeamDSChess */
          /* virtual */ void release()  { delete this; }
    };
    
    class CBeamDSChess_type2 : public CBeamDSChess
    {
       public:
          static CBeamDSChess* instance() { return new CBeamDSChess_type2; }
          /* funzioni virtuali di CBeamDSChess */
          /* virtual */ void release()  { delete this; }
    };
    
    class CBeamDSChess_type3 : public CBeamDSChess
    {
       public:
          static CBeamDSChess* instance() { return new CBeamDSChess_type3; }
          /* funzioni virtuali di CBeamDSChess */
          /* virtual */ void release()  { delete this; }
    };
    
    class Factory1Impl : public Factory1
    {
       public:
          Factory1Impl(void) {}
          ~Factory1Impl(void) {}
          static bool InizializedMap;
          static Factory1Impl* instance_ptr ;
          static Factory1Impl* get_instance() {
             if (instance_ptr == NULL) {
                instance_ptr = new Factory1();
             }
             return instance_ptr;
          }
          /* virtual */ CBeamDSChess * CreateDS( int idOfObjectToCreate );
          std::map<int, BASE_CREATE_FN>  m_pMapRegister;
    };
    Factory1Impl* Factory1Impl::instance_ptr;
    bool Factory1Impl::InizializedMap(false);
    
    //-----------------------------------------------------------------------------
    
    CBeamDSChess* Factory1Impl*::CreateDS( int idOfObjectToCreate ) {
       /* Nessuna eccezione deve uscire dalla funzione */ 
       try {
          BASE_CREATE_FN fz = m_pMapRegister[idOfObjectToCreate];
             return fz();
          } catch ( const std::exception& ) { }
          return 0; 
    }
    
    extern "C" {
       void __declspec(dllexport)  Factory1* FactoryDS() {
          /* Nessuna eccezione deve uscire dalla funzione */ 
          try {
             Factory1Impl::get_instance();
    	if ( InizializedMap == false) {
    
    m_pMapRegister.insert(std::make_pair(tipo1,&CBeamDSChess_type1::instance));
    m_pMapRegister.insert(std::make_pair(tipo2,&CBeamDSChess_type2::instance));
    m_pMapRegister.insert(std::make_pair(tipo3,&CBeamDSChess_type3::instance));
    
               InizializedMap = true;
    	}   
            return Factory1Impl::instance_ptr;
          } catch (const std::exception& ) { }
          return 0; 
         }
    }  // ~extern "C"
    
    
    ////////////////////////////////////////////////////////////////////////////////
    // Applicativo che usa le classi della DLL
    ////////////////////////////////////////////////////////////////////////////////
    // Creo factory
    Factory1* chessFactory = FactoryDS(); // istanza singleton della factory.
    if ( NULL == chessFactory ) throw std::bad_alloc("No Factory1");
    
    // Creo i pezzi.
    CBeamDSChess* type1 = chessFactory->CreateDS(tipo1);
    if ( NULL == type1 ) throw std::bad_alloc("No Factory1");
    
    CBeamDSChess* type2 = chessFactory->CreateDS(tipo2);
    if ( NULL == type2 ) throw std::bad_alloc("No Factory1");
    
    CBeamDSChess* type3 = chessFactory->CreateDS(tipo3);
    if ( NULL == type3 ) throw std::bad_alloc("No Factory1");
    
    // uso i pezzi
    type1->qualcosa();
    type2->qualcosa();
    type3->qualcosa();
    
    // rilascio la memoria usando l'apposito metodo. ASSOLUTAMENTE NO DELETE!!!
    type1->release();
    type2->release();
    type3->release();
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  6. #6
    Utente di HTML.it
    Registrato dal
    Jun 2003
    Messaggi
    4,826
    grazie Shodan.

    il mio problema adesso è questo:
    ho inserito le interfacce nel seguente file:
    codice:
    class CBeamDSChess;
    class CEntityDS;
    
    #include "stdafx.h"
    #include "Factory1.h"
    #include <map>
    
    class CBeamDSChess;
    class CEntityDS;
    
    typedef CEntityDS* (*BASE_CREATE_FN)();
    
    class CFactory1Impl : public CFactory1
    {
       public:
          CFactory1Impl(void) {}
          ~CFactory1Impl(void) {}
          static bool InizializedMap;
          static CFactory1Impl* instance_ptr ;
          static CFactory1Impl* get_instance() {
             if (instance_ptr == NULL) {
                instance_ptr = new CFactory1Impl();
             }
             return instance_ptr;
          }
    	  CEntityDS * CreateDS( int idOfObjectToCreate );
    	  std::map<int, BASE_CREATE_FN>  m_pMapRegister;
    };
    
    CFactory1Impl* CFactory1Impl::instance_ptr;
    
    
    CEntityDS* CFactory1Impl::CreateDS( int idOfObjectToCreate ) {
    	
    	BASE_CREATE_FN fz = m_pMapRegister[0] = CBeamDSChess::instance;
    		return fz();
       return 0; 
    }
    
    extern "C" {
         __declspec(dllexport)CFactory1* FactoryDS() {
    		
    		 CFactory1Impl::m_pMapRegister[0] = CBeamDSChess::instance;
    
    	    return CFactory1Impl::instance_ptr;
          }
          
    }
    nel .h:
    codice:
    #pragma once
    class  CEntityDS;// classe interfaccia.
    
    class CFactory1
    {
    public:
        virtual CEntityDS * CreateDS( int idOfObjectToCreate );
    };
    
    extern "C" {
     __declspec(dllexport)CFactory1* FactoryDS();
    }
    l'errore è questo:

    c:\testdatasetcmpimportapi\CmpFrg\Factory1.cpp(32) : error C2065: 'instance' : undeclared identifier
    c:\testdatasetcmpimportapi\CmpFrg\Factory1.cpp(32) : error C2027: use of undefined type 'CBeamDSChess'

    pardon , nel file CBeamDSChess ho una funzione statica

    static CEntityDS* instance(){return new CBeamDSChess();}


    il casino puo essere nato dal fatto che la mia classe ase è CEntityDS e le derivate derivano da questa, anche CBeamDSChess.


    grazie ancora

  7. #7
    Utente di HTML.it
    Registrato dal
    Jun 2003
    Messaggi
    4,826
    pardon , shodan , il codice è quello che ho postato e ho visto che compila.

    Il problema è che se includo solo un interfaccia, nello specifico "class CBeamDSChess;" non compila, mi da questi errori:


    c:\testdatasetcmpimportapi\CmpFrg\Factory1.cpp(34) : error C2027: use of undefined type 'CBeamDSChess'

    c:\testdatasetcmpimportapi\CmpFrg\Factory1.cpp(34) : error C2065: 'instance' : undeclared identifier


    e il problema è assegnare l'indirizzo della funzione

    codice:
    BASE_CREATE_FN fz = &CBeamDSChess::instance ;
    
    il typedef è :
    
    typedef  CBeamDSChess* (*BASE_CREATE_FN)();
    se metto l'include a "BeamDsChess.h" compila correttamente.

  8. #8
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    codice:
    CFactory1Impl* CFactory1Impl::instance_ptr;
    /* tra qui */
    
    /* e qui */
    
    CEntityDS* CFactory1Impl::CreateDS( int idOfObjectToCreate ) {
    	
    	BASE_CREATE_FN fz = m_pMapRegister[0] = CBeamDSChess::instance;
    		return fz();
       return 0; 
    }
    Nelle righe "tra qui" "e qui" devi mettere la definizione della classe CBeamDSChess o delle sue derivate, altrimenti il compilatore non può sapere com'è fatta la classe solo dalla sua forward declaration, e quindi non può sapere se esiste un metodo instance o no per quella classe.
    Se tu inserisci l'header che hai detto (BeamDsChess.h), il compilatore sa com'è fatta la tua classe per essere istanziata nella DLL per cui il problema è risolto.

    Ne nasce un altro però: quando crei la DLL hai codice fatto e finito, pertanto se non stai attento può capitarti di allocare da un lato (esempio nella DLL) e deallocare dall'altro ( esempio nell'applicazione). Dal momento che gli allocatori sono diversi da debug a release è probabile che l'applicazione vada in crash.
    La regola è: se allochi nella DLL, devi deallocare nella DLL (ragion per cui ho aggiunto il metodo release() nel mio stralcio di codice). COM, Xerces e non solo, adottano questo sistema.

    Se riguardi il mio stralcio di codice d'esempio, io non uso:
    codice:
    CBeamDSChess::instance;
    ma
    codice:
    CBeamDSChess_type1::instance;
    dove CBeamDSChess_type1 è derivata dall'interfaccia CBeamDSChess che avrà solo metodi virtuali puri (no costruttore o distruttore).
    L'applicazione conoscerà solo le classi interfaccia + la funzione di caricamento della factory; la DLL deve conoscere le interfacce astratte, le implementazioni concrete e ovviamente il codice delle funzioni da eseguire.

    Se segui le linee guida del link che ho postato, puoi tranquillamente creare un DLL full release in VC2008 e usarla in VC2010 (o viceversa) senza cambiare una singola linea di codice.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  9. #9
    Utente di HTML.it
    Registrato dal
    Jun 2003
    Messaggi
    4,826
    grazie.
    Scusa se ho cambiato il tuo codice , non avevo capito.
    Quindi dato che non posso spostare tutte le classi da esportare tra
    /* tra qui */

    /* e qui */

    :
    codice:
    CFactory1Impl* CFactory1Impl::instance_ptr;
    /* tra qui */
    
    /* e qui */
    
    CEntityDS* CFactory1Impl::CreateDS( int idOfObjectToCreate ) {
    per motivi aziendali(è un qualcosa che c'è gia') , devo solo fare del refactoring (se si chiama cosi').
    posso creare delle classi che derivano da tutte le interfacce che voglio usare e non implementano nulla tranne la funzione instance di creazione posizionate dove mi hai detto?
    correggimi se sbaglio.

    a questo punto nel mio main posso utilizzare le forward reference senza includere nulla?
    ma un dubbio:
    Se al suo interno una classe CBeamDsChess utilizza una classe esterna , cvector mettiamo ,come fa il compilatore a non includere quella classe ?
    non è importante , ma vorrei capire bene il funzionamento.


    grazie.

  10. #10
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Partiamo dal fondo:
    Se al suo interno una classe CBeamDsChess utilizza una classe esterna , cvector mettiamo ,come fa il compilatore a non includere quella classe ?
    Prima o poi dovrà includerla se vorrà usare un'istanza di quella classe. Il punto è quando.
    Se CBeamDsChess dichiara solo un puntatore o un reference (caso raro), è sufficiente utilizzare una forward declaration. Questo perché il compilatore sa sempre come definire un puntatore o un reference.
    Se però CBeamDsChess dichiara un puntatore o un oggetto di classe cvector e intende usarlo all'interno di un proprio metodo inline, deve sapere com'è fatto cvector.
    codice:
    // Esempio.h
    class CVector;
    class CBeamDsChess {
        public:
            // Il compilatore vede solo un puntatore a CVector e sa come definirlo.
            CVector* CreaVector();
    
            // Qui il compilatore deve sapere com'è fatto CVector per crearne uno.
            // Decommentando la riga si ha errore.
            // CVector* CreaVector2() { return new CVector; }
    
            // Qui il compilatore deve sapere com'è fatto CVector per calcolarne la dimensione.
            // Decommentando la riga si ha errore.
            // CVector CopiaVector();
    };
    
    // Esempio.cpp
    /* Ora il compilatore sa esattamente cos'è CVector e si può usare senza problemi */
    #include <cvector> 
    CVector* CBeamDsChess::CreaVector() {
        return new CVector;
    }
    
    CVector* CBeamDsChess::CreaVector2() {
        return new CVector;
    }
    
    CVector CBeamDsChess::CopiaVector() {
        return CVector(vec); // tramite costruttore di copia.
    }
    a questo punto nel mio main posso utilizzare le forward reference senza includere nulla?
    No. Il main è in un file cpp e un file cpp deve sapere cosa usare e come.

    per motivi aziendali(è un qualcosa che c'è gia') , devo solo fare del refactoring (se si chiama cosi').
    posso creare delle classi che derivano da tutte le interfacce che voglio usare e non implementano nulla tranne la funzione instance di creazione posizionate dove mi hai detto?
    Meglio se chiarisci cosa intendi per "motivi aziendali" (con un breve esempio magari), altrimenti c'è il rischio di intendere e parlare di cose diverse.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.