Använda struct från flera .c/.h filer?

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
xarvox
Inlägg: 137
Blev medlem: 4 augusti 2005, 09:55:07
Ort: växjö

Använda struct från flera .c/.h filer?

Inlägg av xarvox »

Hej alla!

Jag försöker få ihop ett överdrivet avancerat projekt (för att lära mig programmera) men har sprungit in i en vägg...
Plattformen är arduino och språket är nog mestadels c, med lite c++ här o där..

Jag har i en annan tråd här efterfrågat generella råd vid större projekt och fått en massa bra tips om hur dessa bör läggas upp.

Så för att undvika globala variabler så har jag försökt ha "semi-globala" variabler i en struktur (struct) som jag kan läsa ifrån några funktioner samt skriva till med en klass.


Jag har en klass som bland annat sparar millis() till en struct (TimeStruct.Now) samt räknar ut om förfluten tid är större än inställt värde. (if (TimeStruct.Now - lastValue > ValueLimit) { return true; } )

Men jag behöver också läsa från TimeStruct från andra funktioner, vilket kör ihop det för mig..

Problemet som uppstår är att strukturen TimeStruct defnieras flera gånger, "multiple definition of `timeStruct' " vid kompilering.
Jag har så kallade include-guards i alla header-filer..

Mina personliga gissningar är att jag istället borde använda pointers och skicka med strukturen vid funktions-anropet, men trots att jag läst sida upp och ner on pointers, reference och de-reference, så blir jag inte klok på hur detta ska pusslas ihop.
Mina försök att bygga en simpel funktions-modell med pointers har inte varit framgångsrika eller ens lärorika, det har mest varit frustration, förvirring och en massa tid i sökmotorer.



HuvudDokument.ino:

Kod: Markera allt

#include "time.h"
#include "time_structs.h"
myTime Time;

void setup() {
Time.begin();
}

void loop() {

Time.update();

// do stuff

}

time.h:

Kod: Markera allt


#ifndef time_h
#define time_h

#include "Arduino.h"

class myTime {
public: 
  void begin(void);
  void tick(void);
  void update(void);
  bool compare(uint32_t _last, uint16_t _limit);
  bool compare(uint32_t _last, uint32_t _limit);
  
private:
  void timeSetup(void);
  void check(void);
};

#endif // time_h
time.cpp:

Kod: Markera allt


#include "time.h"
#include "time_structs.h"
/*! \class myTime "/time.h"
 *  \brief Time management class
 *  
 *  Sets current time, updates the TimeStruct structure
 *  Sets last usage time, enabling a auto-off feature.
 *  Compares time values, returns difference.
 */

/*! \fn void myTime::timeSetup(void)
 *  \brief If needed, setup for the time class
 *  
 *  Function is private.
 */
void myTime::timeSetup(void) {
  // if needed
}

/*! \fn void myTime::begin(void)
 *  \brief in the beginning there was time with no observer.
 */
void myTime::begin(void) {
  timeSetup();
  tick();
  update();
}
/*! \fn void myTime::tick(void)
 *  \brief increments time
 */
void myTime::tick(void) {
  TimeStruct.Now = millis();
}
/*! \fn void myTime::update(void)
 *  \brief updates last used
 */
void myTime::update(void) {
  tick();
  TimeStruct.lastUpdate = TimeStruct.Now;
}
/*! \fn bool compare(uint32_t _last, uint16_t _limit)
 *  \brief translates long to int (_limit), then runs compare().
 *  
 *  Takes unsigned long, unsigned int as input.
 *    converts unsigned int to unsigned long
 *  runs compare() and returns true if _limit has elapsed
 */
bool myTime::compare(uint32_t _last, uint16_t _limit) {
  uint32_t limit = _limit;
  return (compare(_last, limit));
}
/*! \fn bool compare(uint32_t _last, uint32_t _limit)
 *  \brief compares the difference between _last and now with _limit, returns true if _limit reached.
 *  
 *  Takes unsigned long _last, _limit as input.
 *  calculates the difference between _last and TimeStruct.Now, compares the difference with _limit.
 *  Returns true if _limit elapsed, else returns false.
 */
bool myTime::compare(uint32_t _last, uint32_t _limit) {
  bool result = false;
  if ( TimeStruct.Now - _last >= _limit ) {
    result = true;
  }
  
  return result;
}
/*! \fn void myTime::check(void)
 *  \brief checks if its time for auto_powerOFF.
 *  
 *  Function is private, used within the class only.
 */
void myTime::check(void) {
}

time_structs.h

Kod: Markera allt

#ifndef time_structs_h
#define time_structs_h
#include "Arduino.h"

struct timeStruct {
  uint32_t Now;
  uint32_t lastUpdate;
} timeStruct ;
struct timeStruct TimeStruct = {
  0,
  0
};
#endif // end time_structs_h


ber om ursäkt för kladdig kod, saxade /code-styckena från en arbets-fil (bottom up-approach tror jag), så vissa nödvändiga delar kan saknas, medan en del onödigt finns kvar (tex dokumentationen och klass-deklarationerna).. :P

Slutligen: ta mig inte på orden, jag är inte utbildad programmerare och använder sannolikt fel ord på fel plats här och där, så försök istället förstå vad jag menar (om möjligt).. :)
johano
Inlägg: 1943
Blev medlem: 22 januari 2008, 10:07:45
Ort: Stockholm

Re: Använda struct från flera .c/.h filer?

Inlägg av johano »

Problemet är att du definierar variabeln TimeStruct i en .h fil som inkluderas i fler än en .c/.cpp-fil.

Du bör istället _deklarera_ structen med "extern" i time_structs.h och sedan _definiera_ den i _en_ .c-fil, t.ex. en "time_structs.c"

Kod: Markera allt

// time_structs.h

#ifndef time_structs_h
#define time_structs_h
#include "Arduino.h"

struct timeStruct {
  uint32_t Now;
  uint32_t lastUpdate;
} timeStruct ;

extern struct timeStruct TimeStruct;
#endif // end time_structs_h


// time_structs.c
#include "time_structs.h"

struct timeStruct TimeStruct = {
  0,
  0
};


/johan
Användarvisningsbild
Icecap
Inlägg: 26106
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Använda struct från flera .c/.h filer?

Inlägg av Icecap »

Att deklarera en variabel i en .H-fil får väl ändå anses fel.

Att typdeklarera den i sagda .H-fil är däremot alldeles rätt.

Om man t.ex. skickar parameter från "clock"-filerna i form av en variabel struktur kan man deklarera i mottagaren att man vill ha t.ex. en kopia av klockan till samma struktur, man kan även få adressen till "original"strukturen och jobba där (fult men kan fungera bra ändå).

Om man vill använda variabelstrukturen "globalt" fastän den i grunden är lokal kan man göra just så att den typdefinieras i "Clock.h", deklareras i "Clock.c" som typen som är typdefinierat och i de delar som behöver tillgång (till t.ex. systemtiden) kan man deklarera samma typdefinition men som extern.
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Använda struct från flera .c/.h filer?

Inlägg av arvidb »

Du definierar faktiskt två variabler av typ "struct timeStruct" i time_structs.h: en vid namn TimeStruct:

Kod: Markera allt

struct timeStruct TimeStruct = {
  0,
  0
};
och en vid namn timeStruct (och nu blir det knepigt :D):

Kod: Markera allt

struct timeStruct {
  uint32_t Now;
  uint32_t lastUpdate;
} timeStruct ;
Detta krävar lite närmare förklaring tror jag. :) Detta definierar typen struct timeStruct:

Kod: Markera allt

struct timeStruct {
  uint32_t Now;
  uint32_t lastUpdate;
};
Detta definierar typen struct timeStruct och variabeln timeStruct:

Kod: Markera allt

struct timeStruct {
  uint32_t Now;
  uint32_t lastUpdate;
} timeStruct ;
Så ta bort definitionerna av variablerna TimeStruct och timeStruct och behåll bara definitionen av datatypen struct timeStruct.


Variabeldefinitionerna allokerar plats för variabler av typ struct timeStruct, både när ditt "huvuddokument" kompileras och när time.cpp kompileras. När dessa sedan länkas ihop vet inte länkaren vilken definition som den ska använda.

Generella tips: bygg in timeStruct som en del av myTime istället, skriv en myTime::getTime() eller liknande metod, och skicka med en referens till Time till de funktioner som behöver läsa ut tiden (så att de kan göra det med Time.getTime()). Som det är gjort nu är det inget "semi-globalt" (vad nu det betyder ;)) över TimeStruct - det är en global variabel.
xarvox
Inlägg: 137
Blev medlem: 4 augusti 2005, 09:55:07
Ort: växjö

Re: Använda struct från flera .c/.h filer?

Inlägg av xarvox »

Stort tack för er hjälp allesammans!

Det räckte inte med att flytta definitionen till .c-filerna, jag behöver ta bort variabeln från definitionerna.

Det är då sannerligen inte lätt att lära sig själv programmera, utan forum att fråga hade jag gett upp för länge sen!




Förlåt en newbie-fråga, men hur "skickar jag med" en referens?



Jag försöker just nu få en fungerande struktur att skriva mina funktioner i, så exakt hur jag löser de olika bitarna är inte solklart ännu.
Jag har tänkt tanken att få Time-klassen att returnera sådant som jag vill läsa från structs med mina funktioner, men jag har inte kommit så långt ännu, har inte riktigt koll på alla delarna än..
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Använda struct från flera .c/.h filer?

Inlägg av arvidb »

Flytta vilka definitioner? Och vad menas med att "ta bort variabeln från definitionerna"? Anledningen till att jag är så petig med språket här är att jag tror att det ligger lite missförstånd bakom, så det är nog nyttigt att reda ut detta. :)

Ett par viktiga termer: Att deklarera (declare) något betyder att tala om för kompilatorn hur detta något "ser ut" - hur mycket minne det tar upp, vilken datatyp det rör sig om, hur det anropas (om det är en funktion det rör sig om).

Att definiera (define) något är att faktiskt skapa plats för det. (OBS detta är inte samma sak som preprocessordirektivet #define!)

Du kan ha så många deklarationer av en viss sak som du vill (så länge de inte motsäger varandra), men bara en definition av varje variabel och funktion. Man har typiskt deklarationer av datatyper och funktioner (och globala variabler m.h.a. "extern") i headerfiler, och sedan definierar man respektive funktion och variabel i någon .c-fil.


Du skickar med en referens t.ex. såhär (här är foo() en funktion som behöver läsa av tiden och som gör det genom att anropa Time.getTime()):

Kod: Markera allt

void loop(void) {
{
	...
	foo(&Time);
	...
}
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Använda struct från flera .c/.h filer?

Inlägg av arvidb »

Detta är deklarationer:

Kod: Markera allt

struct timeStruct {
  uint32_t Now;
  uint32_t lastUpdate;
};

struct foo;   // Typdeklaration som talar om för kompilatorn att det finns en struct vid namn foo. Denna deklaration är inte komplett eftersom den inte visar vad structen innehåller, så den tillåter bara att man använder pekare till structen, för det vet kompilatorn hur den ska hantera. Bra för "inkapsling" av data.

void bar(int n);   // Funktionsdeklaration som talar om för kompilatorn att det finns en funktion vid namn bar som inte returnerar något och som tar en int som parameter
Detta är definitioner:

Kod: Markera allt

struct timeStruct TimeStruct = {
  0,
  0
};

int x;   // Faktiskt både deklaration och definition i ett

void bar(int n)
{
	n += 1;
}
xarvox
Inlägg: 137
Blev medlem: 4 augusti 2005, 09:55:07
Ort: växjö

Re: Använda struct från flera .c/.h filer?

Inlägg av xarvox »

arvidb skrev:Flytta vilka definitioner? Och vad menas med att "ta bort variabeln från definitionerna"? Anledningen till att jag är så petig med språket här är att jag tror att det ligger lite missförstånd bakom, så det är nog nyttigt att reda ut detta. :)
Jag är tacksam för din petighet! :)

jag flyttade struct timeStruct TimeStruct = {0,0}; från .h till .cpp, jag hade fått för mig att detta kallades definition, men detta är alltså en deklaration.



foo(&TimeStruct); jag skickar med strukturen som parameter som "pointer", men behöver jag inte definiera TimeStruct* lr liknande, så att &namn vet vart det ska kopplas?
Det är just detta som strular till det i mitt huvud, jag ser inte hur den ena delen kopplas till den andra.. :(


Off-topic; jag ser "foo()" nära nog överallt i exempel och guider, är det bara "random" bokstavskombo eller har namnet något syfte eller mening, inbyggd funktion lr liknande?
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Använda struct från flera .c/.h filer?

Inlägg av arvidb »

:)

Foobar

struct timeStruct TimeStruct = {0,0}; allokerar minne för en variabel av typen struct timeStruct så det är mycket riktigt en definition. Man "skapar" en variabel genom att definiera den, därav att jag tyckte att "ta bort variabeln från definitionerna" var ett märkligt uttryck. "Ta bort variabeldefinitionen från övriga deklarationer i .h-filen" kanske är vad du menar? Och sen definierade du ju två variabler i time_structs.h: en TimeStruct och en timeStruct, båda av typen struct timeStruct. Tog du bort båda definitionerna från headerfilen? Så att bara deklarationen av typen struct timeStruct blev kvar?
xarvox skrev:foo(&TimeStruct); jag skickar med strukturen som parameter som "pointer", men behöver jag inte definiera TimeStruct* lr liknande, så att &namn vet vart det ska kopplas?
Du behöver inte deklarera en speciell typ för "pekare till"; har du deklarerat en typ så vet kompilatorn automatiskt hur den ska behandla pekare till variabler av den typen. I din huvudfil så har du redan en definition av en global variabel Time av typen myTime:

Kod: Markera allt

myTime Time;
Därmed vet kompilatorn också vad &Time betyder (nämligen adressen till den plats där den specifika variabeln Time är lagrad).

Du kan för övrigt göra Time mer lokal genom att deklarera den static, så blir den "bara" filglobal och inte global för hela programmet:

Kod: Markera allt

static myTime Time;
xarvox
Inlägg: 137
Blev medlem: 4 augusti 2005, 09:55:07
Ort: växjö

Re: Använda struct från flera .c/.h filer?

Inlägg av xarvox »

Hehe att det var så enkelt.. :) jag har klurat på foo i flera år nu, aldrig hittat någon konkret förklaring..
Typiskt mig också, att inte kunna släppa en obegriplig detalj förrän jag förstår den.. :P

Ber om ursäkt för märkliga uttryck, får skylla på inkompetens.. :)
Jo, jag menade att ta bort variabel-definitionen från den övriga definitionen av TimeStruct. :)


Nuvarande time_structs.h:

Kod: Markera allt

#ifndef time_structs_h
#define time_structs_h
#include "Arduino.h"

struct timeStruct {
  uint32_t Now;
  uint32_t lastUpdate;
} ;

extern struct timeStruct TimeStruct;
#endif // end time_structs_h

..och time_structs.cpp:

Kod: Markera allt

#include "time_structs.h"

struct timeStruct TimeStruct = {
  0,
  0
};
arvidb skrev: Du skickar med en referens t.ex. såhär (här är foo() en funktion som behöver läsa av tiden och som gör det genom att anropa Time.getTime()):

Kod: Markera allt

void loop(void) {
{
	...
	foo(&Time);
	...
}
menar du att i funktionen foo() finns ett "Time.getTime();" eller att raden "foo(&Time); på något vis anropar .getTime?
Jag är inte korkad, bara inte så smart.. :)


Varför vill man undvika globala klasser?
Jag har en vag aning om varför variabler osv inte bör vara globala, att man kan råka skriva till fel variabel osv, men där tar min "expertis" slut..



Än en gång, tusen tack för all hjälp och stöd, det uppskattas!
Användarvisningsbild
Icecap
Inlägg: 26106
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Använda struct från flera .c/.h filer?

Inlägg av Icecap »

foo(&Time); betyder "Hej foo, här har du adressen till Time, då kan du göra din grej däri".

Så &Time är en pekare (Pointer). Och liksom att det är en styrka kan man skjuta sig så in i helvetet i foten om man klantar sig.

Men vid att definiera Time (typedef struct {bla bla bla} Time;) på ett sätt som "alla" känner till (i .H-filen som kan inkluderas) får rutinen (foo) att veta att "här kommer det en adress som pekar på en Time-block" och då kan den (foo) göra vad som ska göras.

Sedan finns det olika skolor om dessa variabler:
* Fri tillgång, gör vad du vill. Detta är just att skicka pekaren.
* Set/Get. Det är att det finns en rutin som tar emot och kollar värden som ska läggas i variabeln samt en som hämtar en kopia.
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Använda struct från flera .c/.h filer?

Inlägg av arvidb »

xarvox skrev:Jo, jag menade att ta bort variabel-definitionen från den övriga definitionen av TimeStruct. :)
Obs att TimeStruct och timeStruct är olika identifierare för kompilatorn. Så när du skriver TimeStruct istället för timeStruct så blir det lite förvirrande för mig. ;)
xarvox skrev:menar du att i funktionen foo() finns ett "Time.getTime();"
Japp!
xarvox skrev:Varför vill man undvika globala klasser?
Huh? Klasser liksom övriga datatyper är med fördel globala. Variabler (oavsett om de refererar en grundläggande datatyp som int, en struct, eller en klassinstans/ett objekt) är det bäst att hålla så lokala som möjligt.

Skilj på klass och objekt/klassinstans: En klass är en mall för en samling data och kod som arbetar med det datat; ett objekt/en klassinstans är faktisk allokerat utrymme för (en omgång av) datat. Det senare kan refereras av en variabel. myTime är en klass (en datatyp), Time är en variabel som refererar en instans av klassen myTime.

På samma sätt alltså som din deklaration av struct timeStruct i time_structs.h är en mall för data, och definitionen av variabeln TimeStruct i time_structs.cpp allokerar utrymme för en omgång av det datat.

En klass är i princip en struct som också innehåller funktionspekare (till klassens funktioner - funktioner som tillhör en klass brukar kallas "(klass)metoder".)
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Använda struct från flera .c/.h filer?

Inlägg av arvidb »

Icecap skrev:Men vid att definiera Time (typedef struct {bla bla bla} Time;) på ett sätt som "alla" känner till (i .H-filen som kan inkluderas) får rutinen (foo) att veta att "här kommer det en adress som pekar på en Time-block" och då kan den (foo) göra vad som ska göras.
Om du definierar variabeln Time i en headerfil och sedan inkluderar filen i mer än en "compilation unit" så får du länkfel i form av "Multiple definition of". Däremot behöver man såklart deklarera klassen myTime i en headerfil så att "alla" känner till hur den ser ut.
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43150
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Använda struct från flera .c/.h filer?

Inlägg av sodjan »

"Multiple definition..." brukar väl bara vara en varning (?). Det brukar
vara en vanlig metod att testa en ny version av en funktion att inkludera
den före den ordinarie vid länkningen för att på så sätt inkludera den istället.
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Använda struct från flera .c/.h filer?

Inlägg av arvidb »

Med ld (gcc) är det ett error som default i alla fall. Det går kanske att ändra med någon flagga? I vilket fall tror jag inte att det skulle hjälpa TS direkt. :)
Skriv svar