Wat deden mensen vóór sjablonen in C ++? [duplicate]

Deze vraag heeft hier al antwoorden :

Reacties

  • dit is waarschijnlijk op dezelfde manier gedaan als in gewone C, zie Generieke code schrijven wanneer uw doelwit een C-compiler is
  • @Telastyn, IIRC, sjablonen waren nieuw in CFront 3 (rond 1990).
  • @Telastyn: rond het jaar 2000 leverden de meeste bestaande C ++ -compilers niet erg goed sjablonen (zelfs als ze beweerden dat ze het leverden, waren de meeste te buggy voor productiegebruik). Sjabloonondersteuning was voornamelijk voor het ondersteunen van algemene containers, maar verre van de vereisten om zoiets als Alexandrescu ' s " Modern C ++ -ontwerp " voorbeelden.
  • Weet u zeker dat codegeneratie tijdens het compileren werd gebruikt voordat de kracht van sjablonen werd gerealiseerd? Ik was toen jong, maar ik heb de indruk dat vroeger alleen de eenvoudigste soorten codegeneratie tijdens het compileren werden gebruikt. Als je echt een programma wilde schrijven om je programma te schrijven, heb je een programma / script geschreven waarvan de uitvoer C-broncode was, in plaats van dit met sjabloontaal te doen.
  • @Joshua – Vragen worden als duplicaat gesloten als de bestaande antwoorden op een andere vraag zijn gericht op de primaire vragen achter de huidige vraag. " Exacte " overeenkomsten zijn niet vereist; het punt is om het OP snel te matchen met antwoorden op hun vraag. Met andere woorden, ja, het ' is een duplicaat. Dat gezegd hebbende, deze vraag riekt naar " OMG! Hoe bestond de wereld vroeger …?! " wat niet ' een erg constructieve vraag is. Doc Brown wees in een eerdere (en nu verwijderde opmerking) aan hoe dat fenomeen commentaar kan beïnvloeden.

Antwoord

Naast de void * -aanwijzer die in het antwoord van Robert staat, werd een techniek als deze gebruikt (Disclaimer: 20 jaar oud geheugen):

#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; } 

Waar ik de exacte preprocessor-magie in collection.h ben vergeten, maar het was zoiets als dit:

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 

Reacties

  • +1 – Er is een boek van Bjarne Stroustrup waarin hij vertelt de geschiedenis van C ++ (jaren geleden gevonden in de universiteitsbibliotheek), en deze techniek werd expliciet omschreven als een motivatie voor de ontwikkeling van sjablonen.
  • Heeft deze techniek een naam? Misschien " X-macros " ?

Antwoord

Het traditionele manier om generieke geneesmiddelen te implementeren zonder generieke geneesmiddelen (de reden dat sjablonen werden gemaakt) is door een lege pointer te gebruiken.

typedef struct Item{ void* data; } Item; typedef struct Node{ Item Item; struct Node* next; struct Node* previous; } Node; 

In dit voorbeeldcode, een binaire boom of dubbelgekoppelde lijst kan worden weergegeven. Omdat item een lege pointer bevat, kan elk gegevenstype daar worden opgeslagen. Natuurlijk zou je tijdens runtime het gegevenstype moeten weten, zodat je het terug kunt casten naar een bruikbaar object.

Opmerkingen

  • Dat werkt voor generieke opslag, maar hoe zit het met generieke functies? @gnat ' s macros kunnen dat aan, maar deze akelige kleine klasse kan ' t. Won ' t leidde dit ook tot een nachtmerrie van strikte aliasproblemen tijdens het verwerken van de gegevens?
  • @MadScienceDreams Waarom kon niet ' Pas je dit toe op functie-adressen?
  • @IdeaHat: Ja, dat zou het zijn. Maar dit alles komt uit een tijd waarin er veel minder nadruk lag op het feit dat de tools de programmeur van zijn eigen fouten moesten redden. Met andere woorden, je moest voorzichtig zijn omdat de taal je veel meer touw gaf om jezelf in de voet te schieten.
  • Je kon het gegevenstype aan de andere kant van de aanwijzer kennen door ergens op te slaan … misschien in een tafel? Misschien een … vtafel? Dit is het soort dingen dat de compiler voor ons wegtrekt. Met andere woorden, voordat compilers met sjablonen en polymorfisme omgingen, moesten we die zelf maken.
  • @IdeaHat: Kijk voor algemene functies naar qsort in de C-bibliotheek. Het kan alles sorteren omdat het een functie-pointer nodig heeft voor de vergelijkingsfunctie en een paar leegte * ' s doorgeeft.

Antwoord

Zoals andere antwoorden aangaven, kunt u void* gebruiken voor algemene datastructuren.Voor andere soorten parametrisch polymorfisme werden preprocessormacros gebruikt als iets veel werd herhaald (zoals tientallen keren). Om eerlijk te zijn, de meeste van de tijd voor gematigde herhaling, mensen gewoon gekopieerd en geplakt, en vervolgens de typen gewijzigd, omdat er veel valkuilen zijn met macros die ze problematisch maken.

We hebben echt een naam voor het omgekeerde van de blub-paradox , waar mensen het moeilijk vinden om zich programmeren in een minder expressieve taal voor te stellen, omdat dit veel op deze site naar voren komt. Als je nog nooit een taal hebt gebruikt met expressieve manieren om parametrisch polymorfisme te implementeren, weet je niet echt wat je mist. Je accepteert gewoon het kopiëren en plakken als enigszins vervelend, maar noodzakelijk.

Er zijn inefficiënties in uw huidige talen naar keuze waarvan u zich nog niet eens bewust bent. Over twintig jaar zullen mensen zich afvragen hoe je ze hebt geëlimineerd. Het korte antwoord is dat je het niet wist, omdat je niet wist dat je het kon.

Reacties

  • 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. Dat lijkt mij precies de blub-paradox (" Talen die minder krachtig zijn dan Blub zijn duidelijk minder krachtig, omdat ze ' opnieuw mist een functie die hij ' s gewend was. ") Het ' zou het tegenovergestelde zijn als de persoon zijn taal kende ' s beperkingen en zich functies kon voorstellen die deze oplossen.
  • Tegenover isn ' t nogal het woord waar ik naar op zoek was, maar het ' komt het dichtst in de buurt dat ik kon verzinnen. De blub-paradox veronderstelt het vermogen om een minder krachtige taal te herkennen , maar ' geeft geen commentaar op de moeilijkheid om programmeren te begrijpen in een. Enigszins contra-intuïtief geeft de mogelijkheid om in een krachtigere taal te programmeren ' niet een evenredig vermogen om in een minder krachtige taal te programmeren.
  • Ik vermoed " converse " is het woord waarnaar u ' zoekt: " Een situatie, object of statement dat het omgekeerde is van een ander of daarmee overeenkomt, maar met bepaalde termen getransponeerd "

Answer

Ik herinner me dat gcc werd geleverd met genclass – een programma dat een reeks parametertypes ( bijv. sleutel en waarde voor een kaart) en een speciaal syntaxisbestand dat een geparametriseerd type beschrijft (bijvoorbeeld een kaart of een vector) en geldige C ++ implementaties genereerde met de parametertypes ingevuld.

Dus als je nodig Map<int, string> en Map<string, string> (dit was niet de daadwerkelijke syntaxis, let wel) je moest voer dat programma twee keer uit om zoiets als map_string_string.h en map_int_string.h te genereren en deze vervolgens in je code te gebruiken.

Zie de man-pagina voor genclass en de documentatie van GNU C ++ Library 2.0 voor meer details.

Answer

[Aan het OP: ik “probeer niet om u persoonlijk te bekritiseren, maar maak uzelf en anderen” bewust van het denken over de logica van de vraag ( s) gevraagd op SE en elders. Vat dit alsjeblieft niet persoonlijk op!]

De titel van de vraag is goed, maar je beperkt de reikwijdte van je antwoorden ernstig door “… situaties op te nemen waarin ze tijdens het compileren code moesten genereren. “Op deze pagina staan veel goede antwoorden op de vraag over hoe je compileercode genereert in C ++ zonder sjablonen, maar om de vraag te beantwoorden die je oorspronkelijk stelde:

Wat deden mensen vóór sjablonen in C ++?

Het antwoord is natuurlijk dat ze (we) ze niet hebben gebruikt. Ja, ik ben ironisch, maar de details van de vraag in het lichaam lijken (misschien overdreven) aan te nemen dat iedereen dol is op sjablonen en dat er nooit zonder hen gecodeerd zou kunnen worden.

Als voorbeeld heb ik veel codeerprojecten in verschillende talen voltooid zonder dat er tijdens het compileren code moest worden gegenereerd, en ik geloof dat anderen dat ook hebben gedaan. Natuurlijk, het probleem dat werd opgelost door sjablonen was een jeuk die groot genoeg was dat iemand er daadwerkelijk aan krabde, maar het scenario dat door deze vraag werd gesteld, bestond grotendeels niet.

Overweeg een vergelijkbare vraag in autos:

Hoe schakelden chauffeurs van de ene versnelling naar de andere, met behulp van een geautomatiseerde methode die voor u schakelde, voordat de automatische transmissie werd uitgevonden?

De vraag is natuurlijk belachelijk. Vragen hoe iemand X deed voordat X werd uitgevonden, is niet echt een geldige vraag. Het antwoord is over het algemeen: “we hebben het niet gedaan en niet gemist omdat we” niet wisten dat het ooit zou bestaan “.Ja, het is gemakkelijk om het voordeel achteraf te zien, maar aan te nemen dat iedereen stond te trappen, wachtend op automatische transmissie of op C ++ -sjablonen, is echt niet waar.

Op de vraag “hoe schakelden automobilisten voordat de automatische transmissie werd uitgevonden?” Kan men redelijkerwijs “handmatig” antwoorden, en dat is het soort antwoorden dat u hier krijgt. Het kan zelfs het soort vraag zijn dat u wilde stellen.

Maar het was niet degene die u wel stelde.

Dus:

V: Hoe gebruikten mensen sjablonen voordat sjablonen werden uitgevonden?

A: Dat hebben we niet gedaan.

V: Hoe gebruikten mensen sjablonen voordat sjablonen werden uitgevonden, wanneer ze nodig hadden gebruik sjablonen ?

A: We niet nodig om ze te gebruiken. Waarom aannemen van wel? (Waarom aannemen dat we dat wel doen?)

V: Wat zijn alternatieve manieren om de resultaten te bereiken die de sjablonen bieden?

A: Er bestaan hierboven veel goede antwoorden.

Denk alstublieft na over logische drogredenen in uw berichten voordat u een bericht plaatst.

[Bedankt! Alsjeblieft, hier is geen kwaad bedoeld.]

Reacties

  • Ik denk dat je ' zijn overdreven pedant. Het OP heeft zijn vraag niet zo goed geformuleerd als hij zou kunnen hebben. Ik denk echter dat het ' vrij duidelijk is dat hij het wilde stellen zoiets als " hoe hebben mensen eerder generieke functies gemaakt sjablonen ". Ik denk dat in dit geval de juiste actie zou zijn geweest om zijn antwoord te bewerken of een opmerking achter te laten in plaats van een nogal betuttelende lezing te geven.

Answer

Afschuwelijke macros hebben gelijk, van http://www.artima.com/intv/modern2.html :

Bjarne Stroustrup: Ja. Als je zegt sjabloontype T, is dat echt de oude wiskundige methode voor alle T. Dat is de manier waarop het wordt beschouwd. Mijn allereerste paper over “C with Classes” (dat evolueerde naar C ++) uit 1981 noemde geparametriseerde typen. Daar heb ik het probleem goed, maar ik heb de oplossing totaal verkeerd. Ik heb uitgelegd hoe je typen kunt parametriseren met macros, en dat was slechte code.

Je kunt hier zien hoe een oude macroversie van een sjabloon werd gebruikt : http://www.xvt.com/sites/default/files/docs/Pwr++_Reference/rw/docs/html/toolsref/rwgvector.html

Antwoord

Zoals Robert Harvey al zei, een leegte pointer is het generieke datatype.

Een voorbeeld uit de standaard C-bibliotheek, hoe een array van dubbel te sorteren met een generieke sortering:

double *array = ...; int size = ...; qsort (array, size, sizeof (double), compare_doubles); 

Waarbij compare_double is gedefinieerd als:

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); } 

De handtekening van qsort is gedefinieerd in stdlib.h:

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *) ); 

Merk op dat er geen type is controleren tijdens het compileren, zelfs niet tijdens runtime. Als je een lijst met strings sorteert met de comparator hierboven die dubbels verwacht, zal het met plezier proberen de binaire representatie van een string als een double te interpreteren en dienovereenkomstig te sorteren.

Antwoord

Een manier om dit te doen is als volgt:

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; \ } 

De DARRAY_TYPEDECL-macro creëert effectief een struct-definitie (in één regel), ter vervanging van name met de naam die u doorgeeft en een array van de type die u doorgeeft opslaat (de name is aanwezig zodat u deze kunt samenvoegen naar de basisstructuurnaam en nog steeds een geldige identificatie hebben – darray_int * is geen geldige naam voor een structuur), terwijl de DARRAY_IMPL-macro de functies definieert die op die structuur werken (in dat geval worden ze opnieuw gemarkeerd als statisch, zodat men noem de definitie slechts één keer en niet alles scheiden).

Dit zou worden gebruikt als:

#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(); 

Antwoord

Ik denk dat sjablonen vaak worden gebruikt als een manier om containertypen met een veel algoritmische waarden zoals dynamische arrays (vectoren), kaarten, bomen, etc. sorteren, etc.

Zonder sjablonen zijn deze implementaties noodzakelijkerwijs op een generieke manier geschreven en krijgen ze net genoeg informatie over het type dat vereist is voor hun domein. Met een vector hebben ze bijvoorbeeld alleen de gegevens nodig om blt “in staat te zijn en ze moeten de grootte van elk item weten.

Stel dat je een containerklasse hebt genaamd Vector die dit doet. Het duurt nietig *. Het simplistische gebruik hiervan zou zijn dat de code van de applicatielaag veel casting doet. Dus als ze Cat-objecten beheren, moeten ze Cat * naar leegte * werpen en overal weer terug. Het bezaaien van applicatiecode met afgietsels heeft duidelijke problemen.

Sjablonen lossen dit op.

Een andere manier om het op te lossen is door een aangepast containertype te maken voor het type dat u in de container opslaat. Dus als je een Cat-klasse hebt, zou je een CatList-klasse maken die is afgeleid van Vector.Je overbelast dan de paar methoden die je gebruikt, en introduceert versies die Cat-objecten gebruiken in plaats van void *. Dus je “zou de Vector :: Add (void *) – methode overbelasten met Cat :: Add (Cat *), die de parameter intern gewoon doorgeeft aan Vector :: Add (). Vervolgens zou je in je applicatiecode de overbelaste versie van Add bij het passeren van een Cat-object en dus casting vermijden. Om eerlijk te zijn, de Add-methode zou geen cast vereisen omdat een Cat * -object zonder cast naar void * wordt geconverteerd. Maar de methode om een item op te halen, zoals de index-overload of een Get () -methode, zou dat wel doen.

De andere benadering, het enige voorbeeld dat ik me herinner uit de vroege jaren 90 met een groot toepassingsraamwerk, is om een aangepast hulpprogramma te gebruiken dat deze typen over klassen heen maakt. Ik geloof dat MFC dit deed. Ze hadden verschillende klassen voor containers zoals CStringArray, CRectArray, CDoulbeArray, CIntArray, enz. In plaats van dubbele code te behouden, deden ze een soort meta-programmering vergelijkbaar met macros, gebruikmakend van een externe tool die de klassen zou genereren. Ze maakten de tool beschikbaar met Visual C ++ voor het geval iemand wilde het gebruiken – ik heb het nooit gedaan. Misschien had ik het moeten doen. Maar in die tijd waren de experts aan het roemen: “Sane subset of C ++” en “You dont need Templates”

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *