Vad ska jag använda för programmeringsmetodik vid många funktioner - C

Permalänk

Vad ska jag använda för programmeringsmetodik vid många funktioner - C

Jag har flera funktioner, i olika .c filer som jag ska tillsammans skapa ett program.

Först har vi main.c. I den ska det vara en while(1)-loop som kör övriga funktioner i andra .c filer. Men jag vill ha det lite mer som det vore objektorienterat, utan att ens känna ett behov utav C++.

Jag har valt C då jag klarar inte av att kunna C++ och Java samtidigt. Java är mitt favoritspråk. Dessutom håller jag på med mycket inbyggda system, vilket C är ett måste för att få ned kostnaden.

Vilken programmeringsmetodik ska jag använda mig av när jag har många funktioner som jag vill köra i C?

Permalänk
Medlem

Kan du expandera? Vad menar du är problemet med många funktioner, och vilken lösning har e.g. Java som du kan relatera till?

Om ditt problem är att det är mycket att komma ihåg om man ska memorera en hel .c fil, så låter det som att du nog inte utnyttjar .h filer till fullo. .h filer kan bland annat användas för att bara göra en delmängd av alla funktioner i .c filen synliga utåt, i.e. som ett slags system för att specifiera vilka funktioner som är privata och vilka som är publika, lite som enkapsulering. Är du duktig och begränsar API ytan kan du då se varje .c fil lite som en klass i OOP, där de flesta funktionerna är interna för klassen och inte behöver förstås av användaren, medan .h filen specifierar det mer begränsade, publika interfacet för "klassen".

Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

Permalänk
Skrivet av Bryal:

Kan du expandera? Vad menar du är problemet med många funktioner, och vilken lösning har e.g. Java som du kan relatera till?

Om ditt problem är att det är mycket att komma ihåg om man ska memorera en hel .c fil, så låter det som att du nog inte utnyttjar .h filer till fullo. .h filer kan bland annat användas för att bara göra en delmängd av alla funktioner i .c filen synliga utåt, i.e. som ett slags system för att specifiera vilka funktioner som är privata och vilka som är publika, lite som enkapsulering. Är du duktig och begränsar API ytan kan du då se varje .c fil lite som en klass i OOP, där de flesta funktionerna är interna för klassen och inte behöver förstås av användaren, medan .h filen specifierar det mer begränsade, publika interfacet för "klassen".

I Java använder jag alltid polymorfism, dvs klass i en klass. Tycker det är helt underbart och man får en väldigt snygg struktur på ett projekt. Allt blir som ett träd med grenar som grenar sig.

Jag använder .h filer. Men det är bara mest bara för att deklarera funktioner. Visst kan jag använda .h filer där jag deklarerar variabler, arrayer, strukturer som globala. Men jag föredrar att ha det privat och inte använda globala variabler alls.

Edit:

Här har vi ett supertydligt exempel på en klass i C. Mycket enkelt och pedagogiskt, jämfört med övriga C programmerare som skriver som krattor. Tror jag aldrig har sett ett sådant glasklart exempel på en C-klass förut

#include <stdio.h> #include <string.h> #include <stdlib.h> struct point { int x; int y; void (*print)(const struct point*); }; void print_x(const struct point* p) { printf("x=%d\n", p->x); } void print_y(const struct point* p) { printf("y=%d\n", p->y); } int main(void) { struct point p1 = { 2, 4, print_x }; struct point p2 = { 7, 1, print_y }; p1.print(&p1); p2.print(&p2); return 0; }

Om vi utgår från detta exempel. Hur skulle då en "klass i en klass" se ut då? Vi säger att jag lägger ut en till struktur.

#include <stdio.h> #include <string.h> #include <stdlib.h> struct ID { int number; int age; void (*print)(const struct point*); }; struct point { int x; int y; void (*print)(const struct point*); struct ID id; }; void print_x(const struct point* p) { printf("x=%d\n", p->x); } void print_y(const struct point* p) { printf("y=%d\n", p->y); } int main(void) { struct point p1 = { 2, 4, print_x }; struct point p2 = { 7, 1, print_y }; p1.print(&p1); p2.print(&p2); return 0; }

Permalänk
Medlem
Skrivet av heretic16:

I Java använder jag alltid polymorfism, dvs klass i en klass. Tycker det är helt underbart och man får en väldigt snygg struktur på ett projekt. Allt blir som ett träd med grenar som grenar sig.

Jag använder .h filer. Men det är bara mest bara för att deklarera funktioner. Visst kan jag använda .h filer där jag deklarerar variabler, arrayer, strukturer som globala. Men jag föredrar att ha det privat och inte använda globala variabler alls.

Edit:

Här har vi ett supertydligt exempel på en klass i C. Mycket enkelt och pedagogiskt, jämfört med övriga C programmerare som skriver som krattor. Tror jag aldrig har sett ett sådant glasklart exempel på en C-klass förut

#include <stdio.h> #include <string.h> #include <stdlib.h> struct point { int x; int y; void (*print)(const struct point*); }; void print_x(const struct point* p) { printf("x=%d\n", p->x); } void print_y(const struct point* p) { printf("y=%d\n", p->y); } int main(void) { struct point p1 = { 2, 4, print_x }; struct point p2 = { 7, 1, print_y }; p1.print(&p1); p2.print(&p2); return 0; }

Om vi utgår från detta exempel. Hur skulle då en "klass i en klass" se ut då? Vi säger att jag lägger ut en till struktur.

#include <stdio.h> #include <string.h> #include <stdlib.h> struct ID { int number; int age; void (*print)(const struct point*); }; struct point { int x; int y; void (*print)(const struct point*); struct ID id; }; void print_x(const struct point* p) { printf("x=%d\n", p->x); } void print_y(const struct point* p) { printf("y=%d\n", p->y); } int main(void) { struct point p1 = { 2, 4, print_x }; struct point p2 = { 7, 1, print_y }; p1.print(&p1); p2.print(&p2); return 0; }

Ok, fortfarande inte helt säker på vad du är ute efter, men kan iallafall kommentera lite på vad du skrivit här.

1. Koden i ditt andra exempel använder inte polymorfism, utan komposition. Komposition är när man har en klass/strukt som har en annan klass/strukt som fält. Detta används som ett alternativ till arv för att bygga upp komplexa typer. Som du märkt funkar det utmärkt att använda komposition i ett imperativt språk som C.

Polymorfism är något helt ortogonalt. Polymorfism, också kallat "generics" av vissa, är ett sätt att skriva en generell implementation av en funktion/metod/klass, och återanvända den för mer specialiserade instanser av typen. Ett exempel är Java klassen "ArrayList" som är polymorfisk över elementtypen. Man skriver klassen en gång, generell för vilken elementtyp som helst, sen kan användare av klassen ha vilka instanser som helst, e.g. "ArrayList<Integer>" eller "ArrayList<ArrayList<A>>". Liknande, funktionen "id" i Haskell som har typen "forall a. a -> a". C har mycket beklagligen ingen polymorfism, varför man istället behöver casta värden så ofta, e.g. efter en malloc.

2. Att använda .h filer främst för att deklarera funktioner, och att inte använda globala variabler tycker jag du gör helt rätt i! "Publika" strukter och konstanter passar också bäst att ha här.

3. Att ha "metoder" i C, i.e. en strukt med fält av funktionstyp, är inte särskiljt praktiskt för det mesta. Det må se väldigt OOP och fint ut, men det går väldigt mycket mothårs i C. Det kan vara bra att ha ibland, men jag tror att du oftast kommer finna att en "dummare" lösning blir smidigare i det stora hela. Om du vill fortsätta på detta spår kan jag iallafall påpeka att ett makro hade kunnat minska repetition en del!

#define MTD(obj, method, ...) (obj).method(&(obj), __VA_ARGS__) // För att göra om myCoolObjectWithALongName.doThing(&myCoolObjectWithALongName, 123) // Till MTD(myCoolObjectWithALongName, doThing, 123)

Jag hade nog rekommenderat att du kikar en gång till på C++ dock. Ingen som tvingar dig att lära dig hela språket (förutom nördar på internet)! Att bara använda det som ett "C med klasser" kan räcka långt. Jag hade iallafall saknat polymorfism väldigt i C till skillnad mot C++ (även om C++s typsystem fortfarande lämnar mycket att begära).

Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

Permalänk
Skrivet av Bryal:

Ok, fortfarande inte helt säker på vad du är ute efter, men kan iallafall kommentera lite på vad du skrivit här.

Jag är ute efter ett sätt att programmera i C som gör det lättare att få en struktur. Istället för att ha bara massa funktioner och funktionsdeklerationer i .h filer.

Citat:

1. Koden i ditt andra exempel använder inte polymorfism, utan komposition. Komposition är när man har en klass/strukt som har en annan klass/strukt som fält. Detta används som ett alternativ till arv för att bygga upp komplexa typer. Som du märkt funkar det utmärkt att använda komposition i ett imperativt språk som C.

Polymorfism är något helt ortogonalt. Polymorfism, också kallat "generics" av vissa, är ett sätt att skriva en generell implementation av en funktion/metod/klass, och återanvända den för mer specialiserade instanser av typen. Ett exempel är Java klassen "ArrayList" som är polymorfisk över elementtypen. Man skriver klassen en gång, generell för vilken elementtyp som helst, sen kan användare av klassen ha vilka instanser som helst, e.g. "ArrayList<Integer>" eller "ArrayList<ArrayList<A>>". Liknande, funktionen "id" i Haskell som har typen "forall a. a -> a". C har mycket beklagligen ingen polymorfism, varför man istället behöver casta värden så ofta, e.g. efter en malloc.

Ok. Då vet man det. Tack! När jag googlar polymorfism så kommer det upp mycket bilder på klass som har fält som innehåller en klass, där man kan "hoppa" sig fram och tillbaka.

Okej. Så är det därför C inte har Polymorfism, att det har med att man riskerar att misshandla minnet? Som jag vet så skall malloc undvikas till varje pris inom inbyggda system. Det har med att malloc är en osäkerhet är det gäller att allokera minnet. Vid höga hastigheter så kanske man inte får den minne som man har önskat sig och då får man krasch.

Citat:

2. Att använda .h filer främst för att deklarera funktioner, och att inte använda globala variabler tycker jag du gör helt rätt i! "Publika" strukter och konstanter passar också bäst att ha här.

Jag försöker hålla mig väldigt enkel när jag programmerar. Det ska vara pedagogiskt att läsa. Jag använder typ aldrig goto eller static, trots att dessa brukar väl användas ofta. Framför allt i linuxkärnan och Lapack.

Citat:

3. Att ha "metoder" i C, i.e. en strukt med fält av funktionstyp, är inte särskiljt praktiskt för det mesta. Det må se väldigt OOP och fint ut, men det går väldigt mycket mothårs i C. Det kan vara bra att ha ibland, men jag tror att du oftast kommer finna att en "dummare" lösning blir smidigare i det stora hela. Om du vill fortsätta på detta spår kan jag iallafall påpeka att ett makro hade kunnat minska repetition en del!

Så du menar en programmeringsnivå med en nybörjare är betydligt bättre än en mer avancerad? När jag var ny i programmeringsvärlden så visste jag inte var OOP var. Jag gjorde bara funktioner. Programmen fungerande och såg riktigt enkla ut. Men det blir funktioner överallt istället. Detta vill jag undvika denna gång med C. Jag körde Python då.

Citat:

Jag hade nog rekommenderat att du kikar en gång till på C++ dock. Ingen som tvingar dig att lära dig hela språket (förutom nördar på internet)! Att bara använda det som ett "C med klasser" kan räcka långt. Jag hade iallafall saknat polymorfism väldigt i C till skillnad mot C++ (även om C++s typsystem fortfarande lämnar mycket att begära).

Problemet är att jag har skrivit ett bibliotek i C som jag vill använda och C++ har jag ingen nytta utav då jag programmerar för inbyggda system. Vilket är alltid C som gäller. Nu programmerar jag i system som kan använda sig av C++, men då får man själv modifiera lite. Något som tillverkaren inte rekommenderar. STM32 är tillverkaren.

För mig är C ett litet och enkelt språk för dom lilla och små projekten. Dessutom det som jag älskar verkligen med C, än fast jag VET att C++ är bättre, är att C är så konservativt och ändrar sig inte. Medan C++ så måste man hänga med i utvecklingen. Java är också konservativt och har inte ändrat sig sedan Java 8. Snart kommer Java 12 ut.

Jag gillar konservativa språk. Slipper man bry sig om att hänga med

Permalänk
Medlem
Skrivet av heretic16:

Ok. Då vet man det. Tack! När jag googlar polymorfism så kommer det upp mycket bilder på klass som har fält som innehåller en klass, där man kan "hoppa" sig fram och tillbaka.

Det finns flera olika sorters polymorfism inom programmering. När man pratar om polymorfism i objekt-orienterade sammanhang så är det oftast subtypning man menar, snarare än parametrisk polymorfism (som t.ex. generics i Java). Det innebär förenklat att om du har en klass B som ärver från A så kan du använda en instans av B på ställen där en instans av A förväntas. T.ex. i C++:

class A { public: virtual void someMethod() { std::cout << "A"; }; }; class B : public A { public: void someMethod() { std::cout << "B"; }; }; void f(A &a) { a.someMethod(); } int main() { B b; f(b); // Skriver ut B, även fast f tar en instans av A. }

Det går att göra sånt även i C, men koden kommer bara se ut som att den är skriven av en militant C++-programmerare som tvingats att skriva C.

Permalänk
Medlem
Skrivet av heretic16:

Så du menar en programmeringsnivå med en nybörjare är betydligt bättre än en mer avancerad?

Inte mer avancerad - annorlunda.

Man brukar dela upp programmeringsspråk i två huvudgrupper som var och en består av två undergrupper.
Först har vi de imperativa språken som delas upp i procdurella språk (Fortran, C, Pascal, m.fl.) och objektorienterade språk (Java, Smalltalk, m.fl).
Sen så har vi de deklarativa språken som i sin tur delas upp i funktionella (Scheme, Haskell, SML, m.fl) och logik-orienterade (Prolog)

Varje av dessa grupper innebär olika sätt att programmera och tänka. Ingen av dem är mer avancerad än de andra, bara annorlunda.
Inget av de olika angreppssätten är heller "bättre" i någon slags absolut mening, även om de passar olika bra för olika problem.

Det har sagts att det går att skriva Fortran i vilket språk som helst, och på samma sätt så går det att skriva objektorienterat i vilket språk som helst.

Att försöka skriva objektorienterat i ett språk som inte har stöd för det är dock oftast en dålig idé och gör att man komplicerar saker mer än nödvändigt.

Försök inte skriva Java eller C++ i C. Skriv C i C.

Citat:

Dessutom det som jag älskar verkligen med C, än fast jag VET att C++ är bättre, är att C är så konservativt och ändrar sig inte.

C++ är inte bättre. Större och mer komplext, ja, men inte bättre.
C har ändrats rätt mycket sedan det skapades och står inte stilla det heller, även om inte alla nymodigheter accepteras så snabbt bland användare.

Permalänk
Skrivet av Erik_T:

Inte mer avancerad - annorlunda.

Man brukar dela upp programmeringsspråk i två huvudgrupper som var och en består av två undergrupper.
Först har vi de imperativa språken som delas upp i procdurella språk (Fortran, C, Pascal, m.fl.) och objektorienterade språk (Java, Smalltalk, m.fl).
Sen så har vi de deklarativa språken som i sin tur delas upp i funktionella (Scheme, Haskell, SML, m.fl) och logik-orienterade (Prolog)

Varje av dessa grupper innebär olika sätt att programmera och tänka. Ingen av dem är mer avancerad än de andra, bara annorlunda.
Inget av de olika angreppssätten är heller "bättre" i någon slags absolut mening, även om de passar olika bra för olika problem.

Det har sagts att det går att skriva Fortran i vilket språk som helst, och på samma sätt så går det att skriva objektorienterat i vilket språk som helst.

Att försöka skriva objektorienterat i ett språk som inte har stöd för det är dock oftast en dålig idé och gör att man komplicerar saker mer än nödvändigt.

Försök inte skriva Java eller C++ i C. Skriv C i C.

C++ är inte bättre. Större och mer komplext, ja, men inte bättre.
C har ändrats rätt mycket sedan det skapades och står inte stilla det heller, även om inte alla nymodigheter accepteras så snabbt bland användare.

Okej. Då skriver jag C i C Hur gjorde de gamla ID-software utvecklarna då när dem skrev C-kod för Doom och Quake 3?

Jag uppfattar C++ bättre än C då C++ kan allt det C kan ++ lite till. Men jag föredrar C av just att det är "embedded" prioriterar C före C++ av diverse anledningar.

Permalänk
Skrivet av perost:

Det finns flera olika sorters polymorfism inom programmering. När man pratar om polymorfism i objekt-orienterade sammanhang så är det oftast subtypning man menar, snarare än parametrisk polymorfism (som t.ex. generics i Java). Det innebär förenklat att om du har en klass B som ärver från A så kan du använda en instans av B på ställen där en instans av A förväntas. T.ex. i C++:

class A { public: virtual void someMethod() { std::cout << "A"; }; }; class B : public A { public: void someMethod() { std::cout << "B"; }; }; void f(A &a) { a.someMethod(); } int main() { B b; f(b); // Skriver ut B, även fast f tar en instans av A. }

Det går att göra sånt även i C, men koden kommer bara se ut som att den är skriven av en militant C++-programmerare som tvingats att skriva C.

Vi säger att du ska hålla dig till C, men samtidigt vill du bygga upp en struktur. Skulle då då ha typ publika funktioner deklarerade i .h filer och sedan gör du allt tillgängligt för allt och alla, eller hur skulle du göra?

Permalänk

Så här hade jag tänkt

#include <stdio.h> #include <stdlib.h> #include <unistd.h> //Header file for sleep(). man 3 sleep for details. #include <pthread.h> /* * Here is the main function that going to all each .c file from logic folder */ #define sampleTime 0.5 // This should be included inside a header file /* * Here we call the functions from stm32.c, mysql.c, control.c and estimation.c */ void *stm32_mysql_estimation_control(void *vargp) { for (;;) { sleep(sampleTime); printf("Reading stm32 then estimate then control then write to stm32 and then write to database\n"); } return NULL; } /* * Here we call the functions from mobile.c */ void *mobile(void *vargp) { for (;;) { sleep(1); printf("Checking if we have any socket clients connecting. Only allow one client at the time!\n"); } return NULL; } int main() { pthread_t thread_id; printf("Before Thread\n"); pthread_create(&thread_id, NULL, stm32_mysql_estimation_control, NULL); pthread_create(&thread_id, NULL, mobile, NULL); pthread_join(thread_id, NULL); printf("After Thread\n"); exit(0); }

Permalänk

Här är ett exempel på C-kod som jag uppfattar som mycket rörigt!

https://github.com/martinling/libserialport/blob/master/seria...

Kunde man inte hålla det enkelt? Ska det vara så svårt?

Permalänk
Medlem
Skrivet av heretic16:

Här är ett exempel på C-kod som jag uppfattar som mycket rörigt!

https://github.com/martinling/libserialport/blob/master/seria...

Kunde man inte hålla det enkelt? Ska det vara så svårt?

Vad är det med den koden som du tycker är rörigt?
Förutom alla #ifdef satser (som finns där av en anledning) och bristen på kommentarer/dokumentation så ser jag ingenting i den koden som är anmärkningsvärt rörigt.

Permalänk
Medlem

Har inte programmerat mycket i C, finns det inte en bra IDE som strukturerar upp och presenterar koden snyggt åt dig utan att strukturera själva koden?
Utöver det går det inte i ditt fall att lägga function pointers i dina structs?

Permalänk
Medlem
Skrivet av heretic16:

Okej. Då skriver jag C i C Hur gjorde de gamla ID-software utvecklarna då när dem skrev C-kod för Doom och Quake 3?

Jag uppfattar C++ bättre än C då C++ kan allt det C kan ++ lite till. Men jag föredrar C av just att det är "embedded" prioriterar C före C++ av diverse anledningar.

Id software har delat med sig stora delar av deras kod via gihub, tänk på att en del av projekten är skrivna för mycket gammal hårdvara samt OS, med begränsade flyttals operationer och grafik.

https://github.com/id-Software

Skickades från m.sweclockers.com

Permalänk
Hedersmedlem
Skrivet av Erik_T:

Vad är det med den koden som du tycker är rörigt?
Förutom alla #ifdef satser (som finns där av en anledning) och bristen på kommentarer/dokumentation så ser jag ingenting i den koden som är anmärkningsvärt rörigt.

Jag kan faktiskt hålla med att det är rörig kod. Jag kollade inte många rader för att komma till den slutsatsen.

Att inte typdeffa enums/structs till exempel och behöva skriva enum/struct my_type överallt blir onödigt och fult. Att skriva allt med små bokstäver och inte vara konsekvent med understrecken är också rörigt.

If-satsen utan måsvingar är inte snyggt och bara lataktigt, uppmanar till jobbiga buggar också.

Till en början

EDIT:
Oj, skrollade en bit ner och ser användning av goto... Ja, det här är ingen kod som imponerar.

Permalänk
Hedersmedlem

struct point { int x; int y; void (*print)(const struct point*); };

Det här är ett jättedåligt sätt att göra polymorfism i C, av flera anledningar.

Vi börjar med effektivitet. Det är relevant eftersom du skriver att du vill använda C för att skapa kompakt kod med små systemkrav.

Under kategorin effektivitet börjar vi med minne. Att behöva använda RAM-minne för att lagra instruktionspekare, och då dessutom i varenda objekt skalar inte särskilt väl. I ditt exempel har du bara en funktion, men i något som vore mer än ett leksaksexempel så hade du haft fler funktioner.

Om du har många instanser av denna datastruktur så blir detta rätt mycket minne över tid. Du kör kanske inte slut på ditt RAM-minne, däremot kanske du får använda ett långsammare cache i din CPU, eller inget cache alls, vilket gör att din (begränsade) CPU inte kan jobba lika snabbt.

Sen får vi inte glömma alla möjligheter att göra fel på. Varje gång du skapar ett objekt måste du komma ihåg att uppdatera denna tabell med pekare. Vilket också gör det dyrare CPU-mässigt att skapa nya objekt. Helt i onödan ofta.

Detta kan man mitigera delvis genom att skapa ett "klassobjekt" som man pekar till som innehåller en tabell med funktioner. Det är också ungefär så som t.ex. C++ implementerar "virtuella metoder" i verkligheten.

Med det sagt så är det inte polymorfism som du egentligen verkar vara ute efter, utan mer ett sätt att organisera sig.

För att svara på det som egentligen verkar vara kärnan till ditt problem, hur organiserar man sin kod på ett bra sätt i C? Ett bra sätt att tänka på är att läsa på om modulär programmering. Det går ut på att man delar upp sitt program i små beståndsdelar, med väl definierade gränssnittsytor mellan modulerna.

Permalänk
Medlem
Skrivet av Shimonu:

Jag kan faktiskt hålla med att det är rörig kod. Jag kollade inte många rader för att komma till den slutsatsen.

Att inte typdeffa enums/structs till exempel och behöva skriva enum/struct my_type överallt blir onödigt och fult. Att skriva allt med små bokstäver och inte vara konsekvent med understrecken är också rörigt.

If-satsen utan måsvingar är inte snyggt och bara lataktigt, uppmanar till jobbiga buggar också.

Till en början

EDIT:
Oj, skrollade en bit ner och ser användning av goto... Ja, det här är ingen kod som imponerar.

Ja, koden är ju inte perfekt, det är det väl ingen som har påstått, men....
Jag har nog grävt i för mycket kod som verkligen varit rörig för att ens reagera på sådana där småsaker.

Permalänk
Hedersmedlem
Skrivet av Erik_T:

Ja, koden är ju inte perfekt, det är det väl ingen som har påstått, men....
Jag har nog grävt i för mycket kod som verkligen varit rörig för att ens reagera på sådana där småsaker.

Efter att ha varit på ställen där kod ska leva upp till en kodstandard kan jag säga att jag snabbt reagerar

Permalänk
Medlem
Skrivet av Bryal:

Polymorfism är något helt ortogonalt. Polymorfism, också kallat "generics" av vissa, är ett sätt att skriva en generell implementation av en funktion/metod/klass, och återanvända den för mer specialiserade instanser av typen. Ett exempel är Java klassen "ArrayList" som är polymorfisk över elementtypen. …

Nitpick: Arv (inheritance) och överlagring är det som brukar avses när man snackar om polymorfi i OOP. Generics/parametric polymorphism är ett bredare koncept som även återfinns i rent funktionella programmeringsspråk som Haskell

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av Teknocide:

Nitpick: Arv (inheritance) och överlagring är det som brukar avses när man snackar om polymorfi i OOP. Generics/parametric polymorphism är ett bredare koncept som även återfinns i rent funktionella programmeringsspråk som Haskell

Ja det har du helt rätt i. Bara jag som sitter så sällan med OOP och subtyping & co att parametrisk polymorfism är det enda som dyker upp i huvudet när jag tänker på ordet.

Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

Permalänk
Skrivet av pv2b:

struct point { int x; int y; void (*print)(const struct point*); };

Det här är ett jättedåligt sätt att göra polymorfism i C, av flera anledningar.

Vi börjar med effektivitet. Det är relevant eftersom du skriver att du vill använda C för att skapa kompakt kod med små systemkrav.

Under kategorin effektivitet börjar vi med minne. Att behöva använda RAM-minne för att lagra instruktionspekare, och då dessutom i varenda objekt skalar inte särskilt väl. I ditt exempel har du bara en funktion, men i något som vore mer än ett leksaksexempel så hade du haft fler funktioner.

Om du har många instanser av denna datastruktur så blir detta rätt mycket minne över tid. Du kör kanske inte slut på ditt RAM-minne, däremot kanske du får använda ett långsammare cache i din CPU, eller inget cache alls, vilket gör att din (begränsade) CPU inte kan jobba lika snabbt.

Sen får vi inte glömma alla möjligheter att göra fel på. Varje gång du skapar ett objekt måste du komma ihåg att uppdatera denna tabell med pekare. Vilket också gör det dyrare CPU-mässigt att skapa nya objekt. Helt i onödan ofta.

Detta kan man mitigera delvis genom att skapa ett "klassobjekt" som man pekar till som innehåller en tabell med funktioner. Det är också ungefär så som t.ex. C++ implementerar "virtuella metoder" i verkligheten.

Med det sagt så är det inte polymorfism som du egentligen verkar vara ute efter, utan mer ett sätt att organisera sig.

För att svara på det som egentligen verkar vara kärnan till ditt problem, hur organiserar man sin kod på ett bra sätt i C? Ett bra sätt att tänka på är att läsa på om modulär programmering. Det går ut på att man delar upp sitt program i små beståndsdelar, med väl definierade gränssnittsytor mellan modulerna.

Wow! Då vet jag att OOP drar mycket minne och risken för att minnet ska ätas upp, är stort med OOP

Då jag ska jag programmera enkelt med C.

Permalänk
Hedersmedlem
Skrivet av heretic16:

Wow! Då vet jag att OOP drar mycket minne och risken för att minnet ska ätas upp, är stort med OOP

Då jag ska jag programmera enkelt med C.

Nja, dra inte på för stora växlar på det här. Det sättet du implementerade din datatyp på var ineffektivt, inte OOP i allmänhet.

(Sedan är ju OOP dåligt av helt andra anledningar men det är en annan fråga.)

Permalänk
Medlem

Det går rätt bra att skriva det förhållandevis likt OOP-språk med fristående enheter, vars publika funktioner opererar på kontext i form av structar, där ett specifikt enhets publika funktioner får ett lämpligt prefix för att undvika kollision, som t.ex:

/* my_unit.h */ typedef struct MyUnit_ { int some_value; } MyUnit; int my_unit_do_something(const MyUnit *context); ... /* my_unit.c */ static int private_function(int value); int my_unit_do_something(const MyUnit *context) { return private_function(context->value); } static int private_function(int value) { return (int)(value * 3.1415f); }

Vill du dölja din structdefinition går det bra om du använder dynamisk minnesallokering, ex.

/* my_unit.h */ typedef struct MyUnit_ MyUnit; MyUnit *my_unit_create(); void my_unit_destroy(MyUnit *self); ... /* my_unit.c */ struct MyUnit_ { char *data; ssize_t data_size; }; MyUnit * my_unit_create() { MyUnit *self = calloc(1, sizeof(MyUnit)); self->data_size = 24; self->data = calloc(self->data_size, sizeof(MyUnit)); return self; } void my_unit_destroy(MyUnit *self) { free(self->data); free(self); }

En nackdel med C är att det är omständligt (en del boilerplatekod) att sätta upp structar med virtuella funktioner/funktionspekare vilket göra att det inte är lika vanligt, vilket i sin tur leder till att det är svårt att mocka bort objekt i test etc. Å andra sidan är det enkelt att hålla koll på vad som händer (med reservation för typkonverteringar) eftersom man måste skriva allt explicit. Att skriva C i C är dock ett rimligt råd, dvs låtsas inte att det är något annat språk. Du kan göra delar av det ex. C++ gör med klasshierarki etc manuellt men det riskerar att bli mycket jobb om du vill få ut något av det, undrar du hur mycket kan du ju alltid kolla på GObject.
I slutändan är väl det viktigaste att du har koll på vad som finns var om det är småprojekt, om någon annan ska behöva se det är det ju trevligt om det har högre läsbarhet en t.ex detta.

Permalänk
Medlem

Finns det verkligen fortfarande embedded utvecklingsmiljöer som inte stödjer C++?

Permalänk
Skrivet av jensa86:

Finns det verkligen fortfarande embedded utvecklingsmiljöer som inte stödjer C++?

PIC vet jag om att dem stödjer inte C++. Men de flesta utvecklingsmiljöer kan köra C++, trots att C anges vara som standard.

Värt att notera så brukar även Java komma i kontakt med embedded också. Men då är det oftast lite större inbyggda system t.ex. skärmar osv.

MicroEJ är ett företag som programmerar Java i embedded för fullt. Mest för IoT, men även för skrivare osv. Jag har själv skrivit Java kod för inbyggda system. Totalt smärtfritt. Allt bara fungerar.

Men dessas system är dock dyra.

Permalänk

Jag har en nyfiken fråga till er C-programmerare! Vad tycker ni om min programmeringsmetodik?

Vi säger att jag har tre .c filer

#include "headers.h" void printHost(){ printf("Våran host är: %s ", getHost()); }

Och andra .c filen

#include "headers.h" char* host = "Sweclockers! :)"; char* getHost(){ return host; }

Sedan våran tredje .c fil. Den vi ska köra

#include "headers.h" int main(){ printHost(); return 0; }

Och sedan våra headers.h fil.

#include <stdio.h> char* getHost(); void printHost();

Vad tror ni om detta sätt att strukturera upp igenom att använda Java-metoderna getters 'n' setters? Då får man rätt så likt objektorentering, fast med fixerade objekt. Jag är mest bara intresserad att få en fin struktur istället för att ha massvis med globala variabler som man kan komma åt vart som helst.

Ett verkligt exempel. Min C-kod

/* * mysql.c * * Created on: 25 feb. 2019 * Author: dell */ #include "logicFunctions.h" /* * Fields that can be access with getters and setters */ char* host; char* user; char* pass; char* dbname; /* * Constants in this .c files */ const unsigned int port = 3306; const char* socket = NULL; const unsigned int flag = 0; /* * Objects */ MYSQL* conn; /* * Connect to our mysql server */ int connectMySQL() { conn = mysql_init(NULL); if (!(mysql_real_connect(conn, host, user, pass, dbname, port, socket, flag))) { fprintf(stderr, "Error: %s [%d]\n", mysql_error(conn), mysql_errno(conn)); return -1; }else{ printf("Connected to database %s via the user %s \n", dbname, user); } return 0; } /* * Disconnect from mysql server */ int disconnectMySQL() { //mysql_free_result(res); mysql_close(conn); return 0; } /* * Write our query */ int writeQuery(char* query) { if (mysql_query(conn, query)) { fprintf(stderr, "%s\n", mysql_error(conn)); return -1; }else{ printf("Writing to the database\n"); } return 0; } /* * Getters and setters */ char* getHost(){ return host; } void setHost(char* host_){ host = host_; } char* getUser(){ return user; } void setUser(char* user_){ user = user_; } char* getPass(){ return pass; } void setPass(char* pass_){ pass = pass_; } char* getDbname(){ return dbname; } void setDbname(char* dbname_){ dbname = dbname_; }

och headerfilen.

/* * logicFunctions.h * * Created on: 26 feb. 2019 * Author: dell */ #ifndef LOGIC_LOGICFUNCTIONS_H_ #define LOGIC_LOGICFUNCTIONS_H_ /* * Header files for libraries */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libusb-1.0/libusb.h> #include <mysql/mysql.h> /* * For libusb */ int readUSB(); int writeUSB(); int connectUSB(); /* * For MySQL */ int connectMySQL(); int disconnectMySQL(); int writeQuery(char* query); char* getHost(); void setHost(char* host_); char* getUser(); void setUser(char* user_); char* getPass(); void setPass(char* pass_); char* getDbname(); void setDbname(char* dbname_); #endif /* LOGIC_LOGICFUNCTIONS_H_ */

Ja. Koden fungerar.

Permalänk
Medlem
Skrivet av heretic16:

/* * mysql.c * * Created on: 25 feb. 2019 * Author: dell */ #include "logicFunctions.h" /* * Fields that can be access with getters and setters */ char* host; char* user; char* pass; char* dbname; /* * Constants in this .c files */ const unsigned int port = 3306; const char* socket = NULL; const unsigned int flag = 0;

Ett tips är att göra alla variabler du definierar i det yttersta scopet static, annars kommer dom att vara tillgängliga från vilken annan .c-fil som helst (om det inte är din mening att dom ska vara globala.)

Permalänk
Skrivet av pelleplu:

Ett tips är att göra alla variabler du definierar i det yttersta scopet static, annars kommer dom att vara tillgängliga från vilken annan .c-fil som helst (om det inte är din mening att dom ska vara globala.)

Hur menar du?

Skickades från m.sweclockers.com

Permalänk
Medlem
Skrivet av heretic16:

Hur menar du?

Skickades från m.sweclockers.com

Från din kod:

/* * Fields that can be access with getters and setters */ char* host; char* user; char* pass; char* dbname;

Dessa kommer att vara tillgängliga utanför din .c-fil om du inte deklarerar dom med "static" ordet före, dvs:

static char* host; static char* user; static char* pass; static char* dbname;

Samma sak om du har funktioner i din .c-fil som du inte vill ska vara åtkomliga utanför den aktuella filen, då måste dom också deklareras/definieras med static först. Eftersom du verkar vilja isolera åtkomst till själva variablerna med getters/setters så antar jag att du också vill förhindra direkt åtkomst till dom "utifrån".

Permalänk
Inaktiv
Skrivet av heretic16:

Okej. Då skriver jag C i C Hur gjorde de gamla ID-software utvecklarna då när dem skrev C-kod för Doom och Quake 3?

Jag uppfattar C++ bättre än C då C++ kan allt det C kan ++ lite till. Men jag föredrar C av just att det är "embedded" prioriterar C före C++ av diverse anledningar.

C++ är inte bättre än C, bara större. I praktiken använder man oftast en definierad delmängd av C++, s.k. C++--.

Jag älskar C och avskyr C++. Jag gillar vad Linus Torvalds säger om C++, att det bästa med att inte använda C++ är att man slipper att ha att göra med de som gillar C++. Linux är skrivet i C.