|
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Enum to string array lookup. Fra : JS |
Dato : 09-08-02 00:25 |
|
Jeg bruger ofte enums med en dertil hørende liste af strings :
enum lookup_no
{
STRING1,
STRING2,
MAX_LOOKUP
};
char* strings[MAX_LOOKUP] =
{
"text1",
"text2"
};
Jeg har kun brug for at kunne slå strenge op med en enum, altså ikke den
anden vej.
Et problem er at hvis listen er meget lang kan det kan være anstrengende at
vedligeholde, kommer man til at slette een bliver det hele forskudt..
Findes der en mere elegant måde at lave dette hvor der stadig er tale om
konstanter ?
Man kunne bruge et map<> men så er det vel ikke konstanter ?
Et problem med at fylde en liste op ved runtime er også at strengene så vil
fylde i memory 2 gange, fordi de både er linket med og bliver oprettet på
heap.
mvh/JS
| |
Poul Christensen (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Poul Christensen |
Dato : 09-08-02 11:21 |
|
Hvis ikke du skal bruge den enum til noget andet kunne du gøre sådan:
const char* string1 = "text1";
const char* string2 = "text2";
Eller med c++:
#include <string>
const std::string string1("text1");
const std::string string2("text2");
| |
Byrial Jensen (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Byrial Jensen |
Dato : 09-08-02 16:31 |
|
JS <mr_nobody@nowhere.com> skrev:
>
> Jeg har kun brug for at kunne slå strenge op med en enum, altså ikke den
> anden vej.
>
> Et problem er at hvis listen er meget lang kan det kan være anstrengende at
> vedligeholde, kommer man til at slette een bliver det hele forskudt..
> Findes der en mere elegant måde at lave dette hvor der stadig er tale om
> konstanter ?
Ja, C99 har et eksempel på netop det i afsnit 6.7.8:
[#33] EXAMPLE 9 Arrays can be initialized to correspond to
the elements of an enumeration by using designators:
enum { member_one, member_two };
const char *nm[] = {
[member_two] = "member two",
[member_one] = "member one",
};
Det virker i C, men C++ tillader vistnok ikke designatorer i
initialiseringer. (Hvis det gør, retter en af gruppens
C++-eksperter mig forhåbentlig).
| |
JS (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : JS |
Dato : 09-08-02 21:54 |
|
"Byrial Jensen" <bjensen@nospam.dk> wrote in message
news:slrnal7nuf.11v.bjensen@ask.ask...
>
> Ja, C99 har et eksempel på netop det i afsnit 6.7.8:
>
> [#33] EXAMPLE 9 Arrays can be initialized to correspond to
> the elements of an enumeration by using designators:
>
> enum { member_one, member_two };
> const char *nm[] = {
> [member_two] = "member two",
> [member_one] = "member one",
> };
>
> Det virker i C, men C++ tillader vistnok ikke designatorer i
> initialiseringer. (Hvis det gør, retter en af gruppens
> C++-eksperter mig forhåbentlig).
Hvis det spiller i C++ så er det jo genialt, den syntax havde jeg ikke set
før.
mvh/JS
| |
![](/images/tree/0.gif) Bjarke Dahl Ebert (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Bjarke Dahl Ebert |
Dato : 09-08-02 22:55 |
|
"JS" wrote:
> "Byrial Jensen" <bjensen@nospam.dk> wrote in message
> news:slrnal7nuf.11v.bjensen@ask.ask...
> > Ja, C99 har et eksempel på netop det i afsnit 6.7.8:
> >
> > [#33] EXAMPLE 9 Arrays can be initialized to correspond to
> > the elements of an enumeration by using designators:
> >
> > enum { member_one, member_two };
> > const char *nm[] = {
> > [member_two] = "member two",
> > [member_one] = "member one",
> > };
> Hvis det spiller i C++ så er det jo genialt, den syntax havde jeg ikke set
> før.
Det er ny C99 syntaks, som ikke virker i C++.
Jeg ville nok lave det således (i henhold til dit KISS princip ![Glad](images/smileys/glad.gif) .
(Put selv det relevante i headerfilen).
enum error_code
{
FOO = 0, // Jeg plejer at sætte eksplicitte konstanter på når de ikke må
ændres
BAR = 1,
// ...
}
// The map. Details missing
std::map<error_code, const char*> error_map;
void mk_error(error_code code, const char* msg)
{
// Indsæt evt. runtime check af at 'code' ikke indsættes mere end en
gang.
error_map[error_code] = msg;
}
void error_init()
{
mk_error(FOO, "The FOO errormessage");
mk_error(BAR, "blabla");
// ...
}
// Idiom for at initialisere. Virker hvis ingen andre "static initializers"
benytter fejlkoder:
int dummy = (error_init(), 0);
På den måde kan man ændre mk_error med skiftende krav. Du kan sågar definere
mk_error som en makro, skulle du få sådanne sære lyster
Opbygningen af map'et tager et (lille) stykke tid på runtime, og
oversættelsen fra fejlkode til fejltekst kommer også til at tage
"suboptimal" tid, men implementationen er nok simplere end en mere statisk
løsning.
Et godt princip, som må være en delmængde af KISS-princippet, er også:
optimér kun når det er nødvendigt.
Mvh. Bjarke
| |
![](/images/tree/0.gif) ![](/images/tree/0.gif) JS (13-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : JS |
Dato : 13-08-02 01:19 |
|
"Bjarke Dahl Ebert" <bebert@worldonline.dk> wrote in message
news:MUW49.8574$G3.1207752@news010.worldonline.dk...
>
> enum error_code
> {
> FOO = 0, // Jeg plejer at sætte eksplicitte konstanter på når de ikke
må
> ændres
> BAR = 1,
> // ...
> }
>
> // The map. Details missing
> std::map<error_code, const char*> error_map;
> void mk_error(error_code code, const char* msg)
> {
> // Indsæt evt. runtime check af at 'code' ikke indsættes mere end en
> gang.
> error_map[error_code] = msg;
> }
>
> void error_init()
> {
> mk_error(FOO, "The FOO errormessage");
> mk_error(BAR, "blabla");
> // ...
> }
Hvordan er scope eller 'memory life' for disse strenge ?
Hvis map indeholder char* så er det vel kun addressen og ikke hele strengen
som bliver kopieret, og eksisterer disse strenge så når man forlader
error_init() ?
Jeg lavede noget lignende inde i en klasse og det virkede tilsyneladende,
men kan man være sikker på at en streng konstant, som ikke er defineret
globalt, har en permanet plads i memory ?
> // Idiom for at initialisere. Virker hvis ingen andre "static
initializers"
> benytter fejlkoder:
> int dummy = (error_init(), 0);
Hvad sker der helt præcist her ?
mvh/JS
| |
![](/images/tree/0.gif) ![](/images/tree/0.gif) ![](/images/tree/0.gif) Bjarke Dahl Ebert (13-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Bjarke Dahl Ebert |
Dato : 13-08-02 01:51 |
|
"JS" <mr_nobody@nowhere.com> wrote in message
news:3d584f9b$0$87310$edfadb0f@dspool01.news.tele.dk...
> > void error_init()
> > {
> > mk_error(FOO, "The FOO errormessage");
> > mk_error(BAR, "blabla");
> > // ...
> > }
>
> Hvordan er scope eller 'memory life' for disse strenge ?
> Hvis map indeholder char* så er det vel kun addressen og ikke hele
strengen
> som bliver kopieret, og eksisterer disse strenge så når man forlader
> error_init() ?
En litteral string lever indtil programmet dør - det er et krav til
compileren. Compileren har også nogle friheder: Den har lov at lægge
strengene i hukommelse/segmenter der ikke kan skrives i (i embeddede
systemer kan det fx være en ROM), og det er "undefined" at skrive til den.
Egentlig burde "blabla" så have typen const char*, men den kan alligevel
fint assignes over i en char* - det er der vistnok historiske grunde til.
Det er under alle omstændigheder programmørens ansvar at man aldrig skriver
til den streng. Compileren har desuden lov til at "merge" identiske strenge,
sådan at "blabla"=="blabla" (dvs. adresserne er ens).
> Jeg lavede noget lignende inde i en klasse og det virkede tilsyneladende,
> men kan man være sikker på at en streng konstant, som ikke er defineret
> globalt, har en permanet plads i memory ?
Jep.
> > // Idiom for at initialisere. Virker hvis ingen andre "static
> initializers"
> > benytter fejlkoder:
> > int dummy = (error_init(), 0);
> Hvad sker der helt præcist her ?
Ja, det er noget fusk ![Blink](images/smileys/wink.gif) , men det er et valg mellem pest og kolera.
I C kan kan kun initialisere en global variabel statisk, dvs. med værdier
kendt på compiletime.
I C++ kan man initialisere en global variabel med et vilkårligt udtryk. Det
hedder muligvis "static initialization" - det kan man i hvert fald kalde
det. Denne mekanisme misbruger vi så ovenfor, fordi sådan en "static
initialization" desværre KUN udføres som del af initialisering af en global
variabel. Vi er dog lidt på skideren, for error_init() returnerer void, og
man kan ikke have en variabel af typen void (hvilket i parantes bemærket
piner mange template-freaks). Men så putter vi den bare ind i udtrykket
(error_init(), 0), som evaluerer til 0. Det er C's (og dermed C++'s)
sekvens-operator: A,B betyder: Evaluer A og evaluer B, og resultatet er B.
Håber det gav lidt mening
Sidebemærkning der ikke har noget med tråd-emnet at gøre:
Angående problemet med void, så er der andre statisk typede sprog, der har
fattet det, og lavet en pæn semantik: ML (og O'Caml mv.). Her er der en
unittype ved navn 'unit', som kun har ét element (dvs. der findes kun en
eneste dims af den type), nemlig ().
Jeg kan ikke se nogen grund til ikke at tillade:
void f(void, int);
void a = ();
void b = f(a, 42);
Sådan ville man jo aldrig skrive selv, men "void" kunne være et konkret
template-argument, og så giver det pludselig mere mening. Der er dog det
problem at "void*" har en speciel betydning (void* er ikke til void, hvad
int* er til int), så det er måske for sent at rette i C++ nu.
Mvh. Bjarke
| |
![](/images/tree/0.gif) ![](/images/tree/0.gif) ![](/images/tree/0.gif) ![](/images/tree/0.gif) Mogens Hansen (13-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Mogens Hansen |
Dato : 13-08-02 16:03 |
|
"Bjarke Dahl Ebert" <bebert@worldonline.dk> wrote in message
news:GKY59.326$I46.127982@news010.worldonline.dk...
> "JS" <mr_nobody@nowhere.com> wrote in message
> news:3d584f9b$0$87310$edfadb0f@dspool01.news.tele.dk...
> > > // Idiom for at initialisere. Virker hvis ingen andre "static
> > initializers"
> > > benytter fejlkoder:
> > > int dummy = (error_init(), 0);
> > Hvad sker der helt præcist her ?
>
> Ja, det er noget fusk ![Blink](images/smileys/wink.gif) , men det er et valg mellem pest og kolera.
Der er ikke behov for fusk, pest eller kolera.
Se slutningen af mit indlæg i denne tråd fra den 9/8.
Det er tydeligt at funktionen "init_enum2text" initialiserer et map og
returnerer indholdet.
Det er tydeligt at det globale map "enum2text" bliver initialiseret med
resultatet af funktionen "init_enum2text".
Der er ikke nogen grund til at være nervøs over performance ved at returnere
et map by value - før andet er påvist.
Dels har compileren mulighed for at eliminere det temporære objekt - og en
god compiler gør det rent faktisk.
Dels vil det temporære objekt kun blive lavet een gang, nemlig under
opstart, hvis compileren ikke eliminerer det, og det lyder ikke sandsynligt
at det udgør en flaskehals.
Bemærk at det måske ikke er smart at have et "const map<...>", fordi element
access (operator[]) er non-const, fordi ikke eksisterende elementer bliver
indsat.
Venlig hilsen
Mogens Hansen
| |
![](/images/tree/0.gif) ![](/images/tree/0.gif) ![](/images/tree/0.gif) Igor V. Rafienko (14-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Igor V. Rafienko |
Dato : 14-08-02 18:32 |
|
[ mr_nobody@nowhere.com ]
[ snip ]
> Hvordan er scope eller 'memory life' for disse strenge ?
scope har ingenting med et objekts levetid å gjøre. Det finnes heller
intet begrep som heter "memory life" på engelsk.
scope betyr litt løst sagt den delen av koden, der et navn er gyldig
og kan brukes til å referere til den samme entiteten.
objektets levetid, eller "storage duration" i C++-terminologi,
betegner tidsperioden der en entitet eksisterer.
Kap 3 av standarden forklarer meget omstendig nøyaktig hva disse
begrepene betyr.
ivr
--
<peder> igorr: tcl ja... det er fra de dypeste avgrunnene i helvete det...
<peder> php er bare fra foajeen
-- pederst på irc
| |
Nicolai Mouritzen (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Nicolai Mouritzen |
Dato : 09-08-02 19:12 |
|
Hvis du kan give en beskrivelse af hvad formålet med enum/streng
konstruktionen er tror jeg du vil opdage at der er en eller flere
alternative måder at gøre det på, det virker som om du har overflødig data i
dit system.
"JS" <mr_nobody@nowhere.com> wrote in message
news:3d52fd1d$0$87280$edfadb0f@dspool01.news.tele.dk...
>
> Jeg bruger ofte enums med en dertil hørende liste af strings :
>
> enum lookup_no
> {
> STRING1,
> STRING2,
> MAX_LOOKUP
> };
>
> char* strings[MAX_LOOKUP] =
> {
> "text1",
> "text2"
> };
>
> Jeg har kun brug for at kunne slå strenge op med en enum, altså ikke den
> anden vej.
>
> Et problem er at hvis listen er meget lang kan det kan være anstrengende
at
> vedligeholde, kommer man til at slette een bliver det hele forskudt..
> Findes der en mere elegant måde at lave dette hvor der stadig er tale om
> konstanter ?
>
> Man kunne bruge et map<> men så er det vel ikke konstanter ?
>
> Et problem med at fylde en liste op ved runtime er også at strengene så
vil
> fylde i memory 2 gange, fordi de både er linket med og bliver oprettet på
> heap.
>
>
>
>
> mvh/JS
>
>
>
| |
JS (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : JS |
Dato : 09-08-02 19:55 |
|
"Nicolai Mouritzen" <nm@adslhome.dk> wrote in message
news:3d540712$0$14382$edfadb0f@dspool01.news.tele.dk...
> Hvis du kan give en beskrivelse af hvad formålet med enum/streng
> konstruktionen er tror jeg du vil opdage at der er en eller flere
> alternative måder at gøre det på, det virker som om du har overflødig data
i
> dit system.
Enum listen kunne f.eks være fejlkoder og text listen de dertil hørende text
beskrivelser.
Hvis man så gemmer fejlkoderne i en fil og senere skal læse dem ind igen og
vise text beskrivelsen, så har man brug for et textarray hvor man kan slå
teksten op.
mvh/JS
| |
![](/images/tree/0.gif) Peter Lykkegaard (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Peter Lykkegaard |
Dato : 09-08-02 19:58 |
|
"JS" <mr_nobody@nowhere.com> wrote in message
news:3d540f33$0$87301$edfadb0f@dspool01.news.tele.dk...
>
> "Nicolai Mouritzen" <nm@adslhome.dk> wrote in message
> news:3d540712$0$14382$edfadb0f@dspool01.news.tele.dk...
> > Hvis du kan give en beskrivelse af hvad formålet med enum/streng
> > konstruktionen er tror jeg du vil opdage at der er en eller flere
> > alternative måder at gøre det på, det virker som om du har overflødig
data
> i
> > dit system.
>
> Enum listen kunne f.eks være fejlkoder og text listen de dertil hørende
text
> beskrivelser.
> Hvis man så gemmer fejlkoderne i en fil og senere skal læse dem ind igen
og
> vise text beskrivelsen, så har man brug for et textarray hvor man kan slå
> teksten op.
>
Brug en Ressource File?
mvh/Peter Lykkegaard
| |
![](/images/tree/0.gif) ![](/images/tree/0.gif) JS (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : JS |
Dato : 09-08-02 20:24 |
|
"Peter Lykkegaard" <polonline@hotmail.com> wrote in message
news:3d5410c2$0$14365$edfadb0f@dspool01.news.tele.dk...
>
> "JS" <mr_nobody@nowhere.com> wrote in message
>> >
> > Enum listen kunne f.eks være fejlkoder og text listen de dertil hørende
> text
> > beskrivelser.
> > Hvis man så gemmer fejlkoderne i en fil og senere skal læse dem ind igen
> og
> > vise text beskrivelsen, så har man brug for et textarray hvor man kan
slå
> > teksten op.
> >
> Brug en Ressource File?
>
Ja det har jo nogle fordele hvis f.eks teksterne skal ændres ofte så er man
fri for at rekompilere, men så er det så blot ressource Filen som er
besværlig at vedligeholde !
Tanken er at bruge enum's rundt omkring i koden hvor der skal rapporteres
fejl, og fejlkoderne skal så dels gemmes numerisk i en fil men også vises i
text form.
mvh/JS
| |
![](/images/tree/0.gif) Nicolai Mouritzen (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Nicolai Mouritzen |
Dato : 09-08-02 21:05 |
|
Hva' med at lave noget i stil med
// WARNING: The code has never been compiled
class Error
{
public:
Error(int index ,const char* text) : m_Index(index), m_Text(text) {};
operator int() const { return m_Index; };
const char* Text() const { return m_Text; };
private:
const char* m_Text;
const int m_Index;
};
så kunne man jo gøre
const Error OutOfMemory(1,"The system is out of memory");
const Error FileCorrupted(2,"The file has been corrupted");
og derefter bruge dem ved
int error = OutOfMemory;
const char* error_text = OutOfMemory.Text();
det burde gøre trikket for en pris på 4 bytes mere per fejl kode, til
gengæld burde den være rigmeligt nem at vedligeholde og syntaksen burde ikke
være alt for fjern fra det du bruger.
Og hvis du er ligeglad med hvilke værdier dine strenge er knyttet til, kan
du jo lade compileren tildele en værdi, igennem en statisk member som du
tæller op.
"JS" <mr_nobody@nowhere.com> wrote in message
news:3d540f33$0$87301$edfadb0f@dspool01.news.tele.dk...
>
> "Nicolai Mouritzen" <nm@adslhome.dk> wrote in message
> news:3d540712$0$14382$edfadb0f@dspool01.news.tele.dk...
> > Hvis du kan give en beskrivelse af hvad formålet med enum/streng
> > konstruktionen er tror jeg du vil opdage at der er en eller flere
> > alternative måder at gøre det på, det virker som om du har overflødig
data
> i
> > dit system.
>
> Enum listen kunne f.eks være fejlkoder og text listen de dertil hørende
text
> beskrivelser.
> Hvis man så gemmer fejlkoderne i en fil og senere skal læse dem ind igen
og
> vise text beskrivelsen, så har man brug for et textarray hvor man kan slå
> teksten op.
>
>
>
> mvh/JS
>
>
| |
Mogens Hansen (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Mogens Hansen |
Dato : 09-08-02 20:34 |
|
"JS" <mr_nobody@nowhere.com> wrote
[8<8<8<]
> char* strings[MAX_LOOKUP] =
const char*
i stedet for char*
[8<8<8<]
> Et problem er at hvis listen er meget lang kan det kan være anstrengende
at
> vedligeholde, kommer man til at slette een bliver det hele forskudt..
> Findes der en mere elegant måde at lave dette hvor der stadig er tale om
> konstanter ?
Selv om det er svært at hamle op med Brial Jensens C99 kode med hensyn til
udtrykskraft og effektivitet, så er nedenstående måske alligevel
interessant.
I forhold til C99 eksemplet håndterer det spredte enum-værdier godt.
Desuden syntes jeg teknikken generelt er nyttig og interessant.
En hurtig analyse af problemet siger at såvel enum-værdierne som
tekststrengene er kendte på compile-time, og konstante på run-time.
Derfor burde der ikke være behov for at bruge run-time dynamiske strukturer.
Det tyder umiddelbart på at det er værd at kigge på templates.
Et problem med enum er at hvis løsningen skal være generel, skal den være
effektiv selvom enum-værdierne ikke er fortløbende. Både effektiv at skrive
og effektiv at bruge med hensyn til tid og plads.
Vi vil gerne have en genbrugelig struktur, som vi kan bruge med mange
enum/tekst typer.
For at udtrykke sammenhængen mellem enum og tekst tydeligt, vil vi gerne
skrive det i en tabel-lignende struktur, så man kan se hvad der hører
sammen.
<enum, tekst og mapnings kode>
enum tag_enum {
ONE = 10,
TWO = 20,
THREE = 30,
FOUR = 40
};
extern const char ONE_TEXT[] = "one";
extern const char TWO_TEXT[] = "two";
extern const char THREE_TEXT[] = "three";
enum2text<
enum_text< tag_enum, ONE, ONE_TEXT>,
enum_text< tag_enum, TWO, TWO_TEXT>,
enum_text< tag_enum, THREE, THREE_TEXT>
> tag_enum_text;
</enum, tekst og mapnings kode>
hvor vi har de 3 elementer:
* selve enum'en
* teksterne (teksten ligger kun eet sted)
* en tabel med mapningen mellem enum-værdi og tekst.
Det kan bruges som et array:
cout << tag_enum_text[ONE] << endl;
Så mangler vi blot de 2 templates enum2text og enum_text, samt et lille
test-program:
<komplet kode>
#include <stdexcept>
#include <iostream>
#include <vector>
#include <cstddef>
using namespace std;
template <typename EnumType>
class null_enum_text
{
public:
bool match(EnumType) const
{ return false; }
const char* text(void) const
{ return 0; }
};
template <typename EnumType, EnumType EnumValue, const char* EnumText>
class enum_text
{
public:
typedef EnumType enum_type;
bool match(EnumType value) const
{ return EnumValue == value; }
const char* text(void) const
{ return EnumText; }
};
template < typename enum_text_0,
typename enum_text_1 = null_enum_text<typename
enum_text_0::enum_type>,
typename enum_text_2 = null_enum_text<typename
enum_text_0::enum_type>,
typename enum_text_3 = null_enum_text<typename
enum_text_0::enum_type>,
typename enum_text_4 = null_enum_text<typename
enum_text_0::enum_type>,
typename enum_text_5 = null_enum_text<typename
enum_text_0::enum_type>
>
class enum2text
{
public:
const char* operator[](typename enum_text_0::enum_type enum_value)
{
if(enum_text_0().match(enum_value)) return enum_text_0().text();
if(enum_text_1().match(enum_value)) return enum_text_1().text();
if(enum_text_2().match(enum_value)) return enum_text_2().text();
if(enum_text_3().match(enum_value)) return enum_text_3().text();
if(enum_text_4().match(enum_value)) return enum_text_4().text();
if(enum_text_5().match(enum_value)) return enum_text_4().text();
throw runtime_error("no text for enum value");
}
};
enum tag_enum {
ONE = 10,
TWO = 20,
THREE = 30,
FOUR = 40
};
extern const char ONE_TEXT[] = "one";
extern const char TWO_TEXT[] = "two";
extern const char THREE_TEXT[] = "three";
enum2text<
enum_text< tag_enum, ONE, ONE_TEXT>,
enum_text< tag_enum, TWO, TWO_TEXT>,
enum_text< tag_enum, THREE, THREE_TEXT>
> tag_enum_text;
int main(int argc, char* argv[])
{
try {
vector<tag_enum> enum_vector;
enum_vector.push_back(ONE);
enum_vector.push_back(TWO);
enum_vector.push_back(THREE);
for(vector<tag_enum>::iterator i = enum_vector.begin();
enum_vector.end() != i; ++i) {
cout << tag_enum_text[*i] << endl;
}
cout << tag_enum_text[ONE] << endl; // zero overhead !!!
cout << tag_enum_text[TWO] << endl;
cout << tag_enum_text[THREE] << endl;
cout << tag_enum_text[FOUR] << endl; // throws !!!
}
catch(const std::runtime_error& x) {
cerr << "runtime error: " << x.what() << endl;
return EXIT_FAILURE;
}
catch(...) {
cerr << "Unknow error" << endl;
return EXIT_FAILURE;
}
}
</komplet kode>
Klassen "enum2text" kan simpelt udvides med et større antal template
parametre.
Bemærk at
cout << tag_enum_text[ONE] << endl;
kan med en god compiler blive _præcis_ samme assembler-instruktioner som
cout << ONE_TEXT << endl;
fordi _alt_ er bestemt på compile-time.
> Man kunne bruge et map<> men så er det vel ikke konstanter ?
Jo, det kan godt være konstanter:
map<lookup_no, const char*> init_enum2text()
{
map<lookup_no, const char*> result;
result[STRING1] = "text1";
result[STRING2] = "text2";
return result;
}
const map<lookup_no, const char*> enum2text = init_enum2text();
>
> Et problem med at fylde en liste op ved runtime er også at strengene så
vil
> fylde i memory 2 gange, fordi de både er linket med og bliver oprettet på
> heap.
Ikke nødvendigvis.
Hvis man gemmer "const char*" fylder det ikke 2 gange.
Venlig hilsen
Mogens Hansen
| |
JS (09-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : JS |
Dato : 09-08-02 22:19 |
|
"Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
news:aj155v$o4p$1@news.cybercity.dk...
>
>
<snip syret template eksempel..>
Ser vældigt sejt ud men noget overkill i dette tilfælde.
Et gennemgående tema i vores kodestandard er 'Keep it simple stupid', så vi
bruger kun templates, macros og pointergymnastik hvis der er en rigtig god
grund til det !
Templates er genialt til mange ting men det er en syntax som mange har svært
ved at læse, især sidst på natten når man har arbejdet over hele dagen.
> >
> > Et problem med at fylde en liste op ved runtime er også at strengene så
> vil
> > fylde i memory 2 gange, fordi de både er linket med og bliver oprettet
på
> > heap.
>
> Ikke nødvendigvis.
> Hvis man gemmer "const char*" fylder det ikke 2 gange.
Ak ja det er jo klart, så der røg jo egentligt min største grund til at det
skulle være const const.
Jeg tror jeg så vil bruge et char* array gemt inde i en klasse så der kun er
read access.
Man kan jo så initialisere pointerne til at pege på nogle const strenge i
constructoren, og det helt simpelt ved at indexere med enum værdierne.
Så er det også forberedt for at nogen pludseligt finder på at strengene skal
læses ind fra en fil....
mvh/JS
| |
Richard Flamsholt (10-08-2002)
![](/App_Themes/Normal/images/Window/Backgrounds/news.gif) | Kommentar Fra : Richard Flamsholt |
Dato : 10-08-02 00:20 |
|
"JS" <mr_nobody@nowhere.com> skrev:
>Jeg bruger ofte enums med en dertil hørende liste af strings :
>
>enum lookup_no
>{
> STRING1,
> STRING2,
> MAX_LOOKUP
>};
>
>char* strings[MAX_LOOKUP] =
>{
> "text1",
> "text2"
>};
Som modstykke til Mogens' elegante C++template-løsning vil jeg nævne et
gammelt C-preprocessor-indianertrick, der er simpelt og effektivt.
Lav en fil hvor alle informationerne indgår samlet på én linie som
parametre i et makro-kald. I dit eksempel har du kun to, men der kunne
sagtens være flere:
errormap.h:
ERRORINFO(STRING1, "text1")
ERRORINFO(STRING2, "text2")
For at definere din enum og text-array skal de rette informationer blot
hales ud af ERRORINFO() på de rette steder:
#define ERRORINFO(key,text) key,
enum lookup_no
{
#include "errormap.h" /* Note: will make range 0..n */
MAX_LOOKUP /* Only if needed */
};
#undef ERRORINFO
#define ERRORINFO(key,text) text,
const char* strings[] =
{
#include "errormap.h"
}
#undef ERRORINFO
Fordelen er, at alt information om hvert element er samlet på én linie i
en enkelt fil, hvorfra den rigtige kode genereres. Det er simpelt og
sikkert at vedligeholde (tilføje, ændre, fjerne) elementer i den fil.
Det skaber overskuelighed.
Ulempen er, at mange programmører finder preprocessoren oldnordisk og
uigennemskuelig. De vil have svært ved at genkende/gennemskue metoden og
vil derfor (med rette) være modstandere af den.
--
Richard Flamsholt
richard@flamsholt.dk - www.richard.flamsholt.dk
| |
|
|