Oppure

Loading
19/10/15 16:41
Roby94
Salve, sto continuando il porting di alcune librerie da C a CPP e vorrei sfruttare al meglio i vantaggi offerti da quest'ultimo rispetto al suo predecessore. In particolare sto cercando di abbandonare l'abbondante utilizzo di funzioni che accettino in ingresso una grande varieta di tipi di parametro a favore di un piu snello template. Il problema è che sto riscontrando diversi problemi nella scrittura di una libreria statica.
#pragma once

#include <avr/io.h>

namespace EEPROM
{
	template <typename T> T Read(uint16_t);
	template <typename T> void Read(uint16_t, T[], uint16_t);
	
	template <typename T> void Write(uint16_t, T);
	template <typename T> void Write(uint16_t, T[], uint16_t);
}

#include "EEPROM.h"

template <typename T> T EEPROM::Read(uint16_t address) {/**/}
template <typename T> void EEPROM::Read(uint16_t address, T array[], uint16_t n) {/**/}

template <typename T> void EEPROM::Write(uint16_t address, T data) {/**/}
template <typename T> void EEPROM::Write(uint16_t address, T array[], uint16_t n) {/**/}

Questi sono i due file della libreria rispettivamente EEPROM.h e EEPROM.cpp.
Se aggiunti ad un progetto ricevo come errore del genere
undefined reference to `void EEPROM::Write<unsigned int>(unsigned int, unsigned int*, unsigned int)'

Ho risolto dichiarando nel file EEPROM.cpp
template void EEPROM::Write(uint16_t, uint16_t[], uint16_t);

per ogni combinazione di tipi.
Tutto questo funziona finché non compilo la libreria e cerco di richiamare la medesima funzione ricevendo lo stesso errore riportato in precedenza.
Ora, io purtroppo in casa non ho un libro sul CPP, su internet inoltre non riesco a trovare una soluzione, noto che nessuno o quasi aggiunge la definizione della funzione per ogni tipo. Cosa sbaglio? Esiste un metodo piu corretto di realizzare quello di cui ho bisogno? Attualmente sto usando GCC rilasciato con Atmel Studio 7.
Vi ringrazio per ogni aiuto.

Roberto
aaa
19/10/15 18:15
pierotofy
Non puoi compilare la libreria in questo modo; i template vengono risolti al momento della compilazione.

Puoi includere la tua libreria inserendo il .h e .cpp nel tuo progetto, ma non puoi compilarla e poi includerla come .lib o .a (non senza dichiarare ogni combinazione di tipi, vedi qui sotto).

Vedi come fa boost. boost.org/doc/libs/1_55_0/more/getting_started/…

Se vuoi compilarla, devi aggiungere le definizioni per i tipi, per ogni combinazione di tipi (come hai già fatto). E' strano che non funzioni, forse se posti i sorgenti e i risultati della compilazione possiamo dirti di più.

Ultima modifica effettuata da pierotofy 19/10/15 18:16
Il mio blog: piero.dev
19/10/15 18:30
Roby94
Grazie piero.
Ho semplicemente aggiunto
template uint8_t EEPROM::Read(uint16_t);
template uint16_t EEPROM::Read(uint16_t);

template void EEPROM::Read(uint16_t, uint8_t[], uint16_t);
template void EEPROM::Read(uint16_t, uint16_t[], uint16_t);

template void EEPROM::Write(uint16_t, uint8_t);
template void EEPROM::Write(uint16_t, uint16_t);

template void EEPROM::Write(uint16_t, uint8_t[], uint16_t);
template void EEPROM::Write(uint16_t, uint16_t[], uint16_t);

dentro il file cpp.
Non riesco a capire cosa dovrei vedere sulla pagina da te postata...
Se volessi optare per l'inclusione manuale mi basterebbe modificare il file h eliminando i template e sostituendoli con le semplici funzioni con i tipi al posto giusto?
es
void EEPROM::Write(uint16_t, uint16_t[], uint16_t);

Cosi facendo si crea tantissima ridondanza, no? Cioè viene riscritta la funzione per ogni combinazione? Non riesco a trovare molte informazioni sui template, tutte le guide su internet che ho consultato o sono incomplete o imprecise.
aaa
19/10/15 20:17
pierotofy
Boost è una libreria che fa ampio uso dei template. Il link ti mostra come viene utilizzato il codice di boost.

Se usi la tua libreria tramite inclusione del file .h e .cpp, non serve che scrivi tutte le combinazioni; l'utente della libreria si occuperà di dichiarare i tipi che servono a lui.

Se la compili devi dichiarare tutte le combinazioni.

Se questo approccio non ti sembra adatto alla situazione, forse la scelta di usare templates non è adatta e li stai usando in maniera sbagliata (da un punto di vista di progettazione).
Il mio blog: piero.dev
19/10/15 20:52
Roby94
Mi spiego, ho necessita di accertare in ingresso diversi tipi di variabili e in base alla loro dimensione allocare il giusto numero di byte. Posso creare una funzione per ogni tipo, e vado a richiamare da ognuna quella più elementare, quindi quella che si occupa di gestire il singolo byte, questo è l'approccio che adopererei in C. Mi è sembrato di capire che con i template posso fare lo stesso lavoro senza ridefinire uno sproposito di funzioni. Ora ho sempre necessità di compilare una libreria statica, quindi se devo andare a ridefinire i costrutti uno ad uno non è un problema ma anche cosi facendo ricevo l'errore sopra citato. Qual'è il formalismo corretto per fare questo?
aaa
19/10/15 21:10
pierotofy
Definisci funzioni diverse in base al tipo che stai gestendo, ad esempio:

ReadUInt8(...
ReadString(...

Ecc.

Poi internamente nella libreria puoi implementare questi metodi usando un generico Read che usa i template.

se devo andare a ridefinire i costrutti uno ad uno non è un problema ma anche cosi facendo ricevo l'errore sopra citato. Qual'è il formalismo corretto per fare questo?


Strano. Difficile dire quale sia il problema se non possiamo provare a compilare il progetto.

Ultima modifica effettuata da pierotofy 19/10/15 21:15
Il mio blog: piero.dev
19/10/15 21:28
Roby94
Grazie mille Piero, e mi scuso se ti sto facendo faticare.
Ho sperimentato un poco e ho notato che se nel file cpp della libreria definisco i costrutti non ricevo errore. In poche parole ho aggiunto una lista del genere
template uint8_t EEPROM::Read<uint8_t>(uint16_t);
template uint16_t EEPROM::Read<uint16_t>(uint16_t);
template uint32_t EEPROM::Read<uint32_t>(uint16_t);
template uint64_t EEPROM::Read<uint64_t>(uint16_t);
template int8_t EEPROM::Read<int8_t>(uint16_t);
template int16_t EEPROM::Read<int16_t>(uint16_t);
template int32_t EEPROM::Read<int32_t>(uint16_t);
template int64_t EEPROM::Read<int64_t>(uint16_t);
template float EEPROM::Read<float>(uint16_t);
template double EEPROM::Read<double>(uint16_t);

Per tutte e 4 le funzioni, è un metodo valido o sarebbe meglio implementarlo come hai suggerito tu?

Grazie ancora per l'aiuto.

Edit:
Ovviamente ora la libreria pesa ben 16kB
Ultima modifica effettuata da Roby94 19/10/15 21:33
aaa
19/10/15 22:56
pierotofy
Bho, è una questione di preferenza.

Quando si tratta di una libreria esterna, preferisco avere funzioni separate invece che usare generiche. Ma questa è una preferenza personale, perchè i templates non mi piacciono un granchè.
Ultima modifica effettuata da pierotofy 19/10/15 22:57
Il mio blog: piero.dev