Kommentarer
- dette ble sannsynligvis gjort på samme måte som i vanlig C, se Å skrive generisk kode når målet ditt er en C-kompilator
- @Telastyn, IIRC, maler var en nyhet med CFront 3 (ca 1990).
- @Telastyn: rundt år 2000 ga de fleste eksisterende C ++ – kompilatorene ikke maler veldig bra (selv om de later til å gi den, var de fleste for buggy til bruk i produksjonen). Malstøtte var for det meste for å støtte generiske containere, men langt fra kravene for å støtte noe sånt som Alexandrescu ' s " Moderne C ++ design " eksempler.
- Er du sikker på at generering av kompileringskode ble brukt før malenes kraft ble realisert? Jeg var ung da, men jeg er under inntrykk av at det i gamle dager bare ble brukt den enkleste typen generering av kompileringskode. Hvis du virkelig ønsket å skrive et program for å skrive programmet ditt, skrev du et program / skript med utdata var C kildekode, i stedet for å gjøre det med malespråk.
- @Joshua – Spørsmål lukkes som duplikat de eksisterende svarene på et annet spørsmål adresserer de primære spørsmålene bak dette spørsmålet. " Nøyaktige " treff er ikke påkrevd; poenget er å raskt matche OP med svar på deres spørsmål. Med andre ord, ja, det ' er et duplikat. Når det er sagt, smiler dette spørsmålet til " OMG! Hvordan eksisterte verden før …?! " som ikke er ' t et fryktelig konstruktivt spørsmål. Doc Brown påpekte i en tidligere (og nå slettet kommentar) hvordan dette fenomenet kan påvirke kommentarer.
Svar
Foruten void *
pekeren som er dekket i Robert sitt svar , ble en teknikk som denne brukt (Ansvarsfraskrivelse: 20 år gammelt minne):
#define WANTIMP #define TYPE int #include "collection.h" #undef TYPE #define TYPE string #include "collection.h" #undef TYPE int main() { Collection_int lstInt; Collection_string lstString; }
Hvor jeg har glemt den nøyaktige preprosessor-magien inni collection.h
, men det var noe sånt som dette:
class Collection_ ## TYPE { public: Collection_ ## TYPE () {} void Add(TYPE value); private: TYPE *list; size_t n; size_t a; } #ifdef WANTIMP void Collection_ ## TYPE ::Add(TYPE value) #endif
Kommentarer
- +1 – Det er en bok av Bjarne Stroustrup der han forteller historien til C ++ (fant den for mange år siden på universitetsbiblioteket), og denne teknikken ble eksplisitt beskrevet som en motivasjon for utvikling av maler.
- Har denne teknikken et navn? Kanskje " X-makroer " ?
Svar
The tradisjonell måte å implementere generiske uten å ha generiske (grunnen til at malene ble opprettet) er å bruke en tom peker.
typedef struct Item{ void* data; } Item; typedef struct Node{ Item Item; struct Node* next; struct Node* previous; } Node;
I dette eksempelkoden, et binært tre eller dobbeltkoblet liste kan være representert. Fordi item
innkapsler en tom peker, kan hvilken som helst datatype lagres der. Selvfølgelig må du kjenne datatypen under kjøretid, slik at du kan kaste den tilbake til et brukbart objekt.
Kommentarer
- Det fungerer for generisk lagring, men hva med generiske funksjoner? @gnat ' s makroer takler det, men denne fryktelige lille klassen kan ' t. Vant ' t dette også føre til et mareritt med strenge aliasing problemer mens du behandler dataene?
- @MadScienceDreams Hvorfor kunne ikke ' t bruker du dette på funksjonsadresser?
- @IdeaHat: Ja, det ville det. Men alt dette kommer fra en tid da det ble mye mindre vekt på å ha verktøyene redde programmereren fra sine egne feil. Med andre ord, du måtte være forsiktig fordi språket ga deg mye mer tau for å skyte deg i foten.
- Du kunne vite datatypen i den andre enden av pekeren ved å lagre et sted .. … kanskje i et bord? Kanskje en … vtabell? Dette er den slags ting kompilatoren trekker bort for oss. Med andre ord, før kompilatorer håndterte maler og polymorfisme, måtte vi lage våre egne.
- @IdeaHat: For generiske funksjoner, se på qsort i C-biblioteket. Det kan sortere hva som helst fordi det tar en funksjonspeker for sammenligningsfunksjonen og sender et par ugyldige * ' s.
Svar
Som andre svar påpekte, kan du bruke void*
til generiske datastrukturer.For andre typer parametrisk polymorfisme ble preprosessor-makroer brukt hvis noe ble gjentatt en mye (som dusinvis av ganger). For å være ærlig, men mesteparten av tiden for moderat repetisjon, kopierte folk bare og limte inn, og endret deretter typene, fordi det er mange fallgruver med makroer som gjør dem problematiske.
Vi trenger virkelig en navn for det omvendte av blub paradox , der folk har vanskelig for å forestille seg programmering på et mindre uttrykksfullt språk, fordi dette kommer mye opp på dette nettstedet. Hvis du aldri har brukt et språk med uttrykksfulle måter å implementere parametrisk polymorfisme på, vet du ikke hva du mangler. Du godtar bare at kopiering og liming er noe irriterende, men nødvendig.
Det er ineffektivitet i de nåværende valget av språk du ikke er klar over ennå. Om tjue år vil folk lure på hvordan du eliminerte dem. Det korte svaret er at du ikke «t, fordi du ikke visste at du kunne.
Kommentarer
-
We really need a name for the opposite of the blub paradox, where people have a hard time imagining programming in a less expressive language, because this comes up a lot on this site.
Det virker akkurat som blub-paradokset for meg (" Språk mindre kraftige enn Blub er åpenbart mindre kraftige, fordi de ' mangler noen funksjoner han ' pleide å. ") Det ' d var det motsatte hvis personen kjente språkets ' s begrensninger og kunne forestille seg funksjoner som løser dem. - Motsatt er ikke ' t ganske ordet jeg gikk etter, men det ' er det nærmeste jeg kunne komme på. Blub-paradokset antar muligheten til å gjenkjenne et mindre kraftig språk, men kommenterer ikke ' om vanskeligheten med å forstå programmering i en. Noe kontraintuitivt gir ikke evnen til å programmere på et kraftigere språk ' en tilsvarende evne til å programmere i en mindre kraftig.
- Jeg mistenker at " samtale " er ordet du ' leter etter: " En situasjon, gjenstand eller uttalelse som er motsatt av en annen eller tilsvarer den, men med visse vilkår transponert "
Svar
Jeg husker da gcc ble levert med genclass
– et program som tok som inngang et sett med parametertyper ( f.eks. nøkkel og verdi for et kart) og en spesiell syntaksfil som beskrev en parameterisert type (for eksempel et kart eller en vektor) og genererte en gyldig C ++ implementering med param-typene fylt ut.
Så hvis du trengte Map<int, string>
og Map<string, string>
(dette var ikke den faktiske syntaksen, husk at) du måtte kjør programmet to ganger for å generere noe sånt som map_string_string.h og map_int_string.h, og bruk disse i koden din.
Se man-siden for genclass
og dokumentasjon fra GNU C ++ Library 2.0 for mer detaljer.
Svar
[Til OP: Jeg prøver ikke å plukke deg personlig, men øke din og andres bevissthet om å tenke på logikken i spørsmålet ( s) spurte på SE og andre steder. Ikke ta dette personlig!]
Tittelen på spørsmålet er bra, men du begrenser omfanget av svarene dine sterkt ved å inkludere «… situasjoner der de trengte kompilering av kodegenerering. «Det er mange gode svar på spørsmålet om hvordan man kan lage generering av kodetid i C ++ uten maler på denne siden, men for å svare på spørsmålet du opprinnelig stilte:
Hva gjorde folk før maler i C ++?
Svaret er selvfølgelig at de (vi) ikke brukte dem. Ja, jeg er tunge i kinnet, men detaljene i spørsmålet i kroppen ser ut til (kanskje overdrevet) å anta at alle elsker maler og at ingen koding noen gang kunne ha blitt gjort uten dem.
Som et eksempel fullførte jeg mange mange kodeprosjekter på forskjellige språk uten å kreve generering av kodetid, og tror andre også har gjort det. Visst, problemet løst med maler var en kløe som var stor nok til at noen faktisk klødde det, men scenariet som ble stilt ved dette spørsmålet var stort sett ikke-eksisterende.
Vurder et lignende spørsmål i biler:
Hvordan skiftet sjåførene fra ett gir til et annet, ved hjelp av en automatisert metode som skiftet gir for deg før automatgiret ble oppfunnet?
Spørsmålet er selvfølgelig dumt. Å spørre hvordan en person gjorde X før X ble oppfunnet, er egentlig ikke et gyldig spørsmål. Svaret er generelt: «vi gjorde ikke det og savnet det ikke fordi vi ikke visste at det noen gang ville eksistere».Ja, det er lett å se fordelen etter det, men å anta at alle sto rundt og sparket i hælene, ventet på automatgir eller på C ++ – maler, er virkelig ikke sant.
På spørsmålet, «hvordan skiftet sjåførene før automatgirkassen ble oppfunnet?» Kan man med rimelighet svare «manuelt», og det er typen du får svar her. Det kan til og med være typen spørsmål du mente å stille.
Men det var ikke den du stilte.
Så:
Q: Hvordan brukte folk maler før maler ble oppfunnet?
A: Vi gjorde ikke det.
Spørsmål: Hvordan brukte folk maler før maler ble oppfunnet, når de trengte å bruk maler ?
A: Vi trengte ikke å bruke dem. Hvorfor anta at vi gjorde det? (Hvorfor anta at vi gjør det?)
Spørsmål: Hva er alternative måter å oppnå resultatene som malene gir?
A: Mange gode svar finnes ovenfor.
Tenk på logiske feil i innleggene dine før du legger ut.
[Takk! Vær så snill, ingen skade ment her.]
Kommentarer
- Jeg tror du ' er utilbørlig pedantisk. OP fraserte ikke spørsmålet hans så godt som han kunne ha. Men jeg tror det ' er ganske klart at han ønsket å spørre noe sånt som " hvordan skapte folk generiske funksjoner før maler ". Jeg tror i dette tilfellet den riktige handlingen ville ha vært å redigere svaret hans eller legge igjen en kommentar i stedet for å holde et ganske nedlatende foredrag.
Svar
Fryktelige makroer er riktig, fra http://www.artima.com/intv/modern2.html :
Bjarne Stroustrup: Ja. Når du sier «maltype», er det egentlig den gamle matematiske «for alle T.» Det er slik det vurderes. Mitt aller første papir om «C med klasser» (som utviklet seg til C ++) fra 1981 nevnte parametrerte typer. Der fikk jeg problemet riktig, men jeg fikk løsningen helt feil. Jeg forklarte hvordan du kan parameterisere typer med makroer og gutt som var elendig kode.
Du kan se hvordan en gammel makroversjon av en mal ble brukt her : http://www.xvt.com/sites/default/files/docs/Pwr++_Reference/rw/docs/html/toolsref/rwgvector.html
Svar
Som Robert Harvey allerede sa, er en ugyldig peker den generiske datatypen.
Et eksempel fra standard C-biblioteket, hvordan man sorterer en matrise med dobbelt med en generisk sortering:
double *array = ...; int size = ...; qsort (array, size, sizeof (double), compare_doubles);
Hvor compare_double
er definert som:
int compare_doubles (const void *a, const void *b) { const double *da = (const double *) a; const double *db = (const double *) b; return (*da > *db) - (*da < *db); }
Signaturen til qsort
er definert i stdlib.h:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *) );
Merk at det ikke er noen type sjekke ved kompileringstid, ikke engang på kjøretid. Hvis du sorterer en liste over strenger med komparatoren ovenfor som forventer dobler, vil den gjerne prøve å tolke den binære representasjonen av en streng som en dobbel og sortere deretter.
Svar
En måte å gjøre dette på er slik:
https://github.com/rgeminas/gp–/blob/master/src/scope/darray.h
#define DARRAY_DEFINE(name, type) DARRAY_TYPEDECL(name, type) DARRAY_IMPL(name, type) // This is one single long line #define DARRAY_TYPEDECL(name, type) \ typedef struct darray_##name \ { \ type* base; \ size_t allocated_mem; \ size_t length; \ } darray_##name; // This is also a single line #define DARRAY_IMPL(name, type) \ static darray_##name* darray_init_##name() \ { \ darray_##name* arr = (darray_##name*) malloc(sizeof(darray_##name)); \ arr->base = (type*) malloc(sizeof(type)); \ arr->length = 0; \ arr->allocated_mem = 1; \ return arr; \ }
DARRAY_TYPEDECL-makroen oppretter effektivt en strukturdefinisjon (i en enkelt linje), og erstatter name
med navnet du passerer, og lagrer en matrise av type
du sender (name
er der slik at du kan sammenkoble den til grunnstrukturenavnet og fortsatt har en gyldig identifikator – darray_int * er ikke et gyldig navn for en struktur), mens DARRAY_IMPL-makroen definerer funksjonene som fungerer på den strukturen (i så fall er de «markert statisk bare slik at man bare ring definisjonen en gang og ikke skille alt).
Dette vil bli brukt som:
#include "darray.h" // No types have been defined yet DARRAY_DEFINE(int_ptr, int*) // by this point, the type has been declared and its functions defined darray_int_ptr* darray = darray_int_ptr_init();
Svar
Jeg tror maler blir brukt mye som en måte å gjenbruke containertyper som har mange algoritmiske verdier som dynamiske matriser (vektorer), kart, trær osv. sortering osv.
Uten maler, inneholder disse nødvendigvis implementeringer skrevet på en generisk måte og får akkurat nok informasjon om typen som kreves for domenet deres. For eksempel, med en vektor, trenger de bare at dataene skal kunne «være i stand, og de trenger å vite størrelsen på hvert element.
La oss si at du har en beholderklasse kalt Vector som gjør dette. Det tar ugyldighet *. Den forenklede bruken av dette vil være at applikasjonslagskoden gjør mye casting. Så hvis de administrerer katteobjekter, må de kaste Cat * til ugyldighet *, og tilbake overalt. Å kaste applikasjonskode med kaster har åpenbare problemer.
Maler løser dette.
En annen måte å løse den på er å lage en tilpasset containertype for den typen du lagrer i containeren. Så hvis du har en Cat-klasse, vil du opprette en CatList-klasse hentet fra Vector.Deretter overbelaster du de få metodene du bruker, og introduserer versjoner som tar Cat-objekter i stedet for ugyldige *. Så du ville overbelaste Vector :: Add (void *) -metoden med Cat :: Add (Cat *), som internt bare sender parameteren til Vector :: Add (). Så i applikasjonskoden din, vil du ringe til overbelastet versjon av Legg til når du sender inn en Cat-gjenstand og unngå dermed å kaste. For å være rettferdig, vil ikke Add-metoden kreve en rollebesetning fordi et Cat * -objekt konverterer til ugyldig * uten en rollebesetning. Men metoden for å hente et element, som indeksoverbelastning eller en Get () -metode, ville det. >
Den andre tilnærmingen, det eneste eksemplet jeg husker fra begynnelsen av 90-tallet med et stort applikasjonsrammeverk, er å bruke et tilpasset verktøy som skaper disse typene over klasser. Jeg tror MFC gjorde dette. De hadde forskjellige klasser for containere som CStringArray, CRectArray, CDoulbeArray, CIntArray, etc. I stedet for å opprettholde duplikatkode, gjorde de noen form for metaprogrammering som ligner på makroer, ved hjelp av et eksternt verktøy som ville generere klassene. De gjorde verktøyet tilgjengelig med Visual C ++ i tilfelle noen ønsket å bruke det – det gjorde jeg aldri. Kanskje jeg burde ha gjort det. Men på det tidspunktet var ekspertene som «Sane subset of C ++» og «You do not need Templates»