/ Forside / Teknologi / Udvikling / C/C++ / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
C/C++
#NavnPoint
BertelBra.. 2425
pmbruun 695
Master_of.. 501
Bech_bb 500
kyllekylle 500
jdjespers.. 500
gibson 300
scootergr.. 300
molokyle 287
10  strarup 270
liste af structs
Fra : Michael Rasmussen


Dato : 23-10-05 19:58

Hej alle,

Jeg har et problem, som jeg ikke lige kan finde en fornuftig løsning på.
Jeg har en række structs defineret, og i en funktion skal jeg kunne
modtage en antal af disse structs. Det skal forstås sådan, at jeg vil
kunne modtage 1..n af disse structs, overført som et array af void *
pointere, og i min funktion skal jeg så kunne caste disse til deres
oprindelige type. Problemet er så blot her, at jeg ikke på forhånd ved,
antallet af structs i arrayet, og ej heller kender indholdet udover, at
det vil indeholde en eller flere af de definerede structs. Var det kun
muligt at sende en struct, ville problemet kunne løses med en union, men
i det her tilfælde dur det selvfølgelig ikke. Mit spørgsmål er nu:
1) Er dette muligt?
2) Hvordan gøres det i praksis?
3) Hvordan kan jeg vide, hvilke structs array indeholder?

--
Hilsen/Regards
Michael Rasmussen
http://keyserver.veridis.com:11371/pks/lookup?op=get&search=0xE3E80917


 
 
Kent Friis (23-10-2005)
Kommentar
Fra : Kent Friis


Dato : 23-10-05 20:18

Den Sun, 23 Oct 2005 20:58:23 +0200 skrev Michael Rasmussen:
> Hej alle,
>
> Jeg har et problem, som jeg ikke lige kan finde en fornuftig løsning på.
> Jeg har en række structs defineret, og i en funktion skal jeg kunne
> modtage en antal af disse structs. Det skal forstås sådan, at jeg vil
> kunne modtage 1..n af disse structs, overført som et array af void *
> pointere, og i min funktion skal jeg så kunne caste disse til deres
> oprindelige type. Problemet er så blot her, at jeg ikke på forhånd ved,
> antallet af structs i arrayet, og ej heller kender indholdet udover, at
> det vil indeholde en eller flere af de definerede structs. Var det kun
> muligt at sende en struct, ville problemet kunne løses med en union, men
> i det her tilfælde dur det selvfølgelig ikke. Mit spørgsmål er nu:
> 1) Er dette muligt?
> 2) Hvordan gøres det i praksis?
> 3) Hvordan kan jeg vide, hvilke structs array indeholder?

Ganske simpelt - det definerer du dig ud af.

C gør ikke arbejdet for dig.

Mvh
Kent
--
Hard work may pay off in the long run, but laziness pays off right now.

Michael Rasmussen (23-10-2005)
Kommentar
Fra : Michael Rasmussen


Dato : 23-10-05 21:26

On Sun, 23 Oct 2005 19:17:33 +0000, Kent Friis wrote:

>
> Ganske simpelt - det definerer du dig ud af.
>
> C gør ikke arbejdet for dig.
>
Ja, se det er jo stor visdom, men det løser jo ikke mit problem
--
Hilsen/Regards
Michael Rasmussen
http://keyserver.veridis.com:11371/pks/lookup?op=get&search=0xE3E80917


Kent Friis (23-10-2005)
Kommentar
Fra : Kent Friis


Dato : 23-10-05 21:34

Den Sun, 23 Oct 2005 22:26:03 +0200 skrev Michael Rasmussen:
> On Sun, 23 Oct 2005 19:17:33 +0000, Kent Friis wrote:
>
>>
>> Ganske simpelt - det definerer du dig ud af.
>>
>> C gør ikke arbejdet for dig.
>>
> Ja, se det er jo stor visdom, men det løser jo ikke mit problem

Ok, så har jeg fejl-vurderet niveauet. Du mangler altså forslag til
hvordan man overfører informationer om et array til en funktion?

En mulighed kunne være at give funktionen to parametre, den ene er dit
array, og den anden er et array af samme størrelse der fortæller typen
af hvert element.

En anden mulighed kunne være at pakke hver void* ind i en struct,
så du kun har et array, der indeholder en række {enum type, void *}.

Mvh
Kent
--
Hard work may pay off in the long run, but laziness pays off right now.

Michael Rasmussen (23-10-2005)
Kommentar
Fra : Michael Rasmussen


Dato : 23-10-05 22:36

On Sun, 23 Oct 2005 20:33:53 +0000, Kent Friis wrote:

>
> Ok, så har jeg fejl-vurderet niveauet. Du mangler altså forslag til
> hvordan man overfører informationer om et array til en funktion?
>
Nej, det er jeg helt med på. Mit problem er mere, hvordan man får
overførst informationen om, hvilke structs arrayet indeholder.

> En mulighed kunne være at give funktionen to parametre, den ene er dit
> array, og den anden er et array af samme størrelse der fortæller typen
> af hvert element.
>
Det er den måde, jeg løser det nu, men jeg synes, den virker lidt
ufleksibel, og ikke i rigtig C-stil.

> En anden mulighed kunne være at pakke hver void* ind i en struct, så
> du kun har et array, der indeholder en række {enum type, void *}.
>
Det lyder som en bedre og mere fleksibel løsning.

Hvad jeg i virkeligheden kunne ønske mig var, hvis man kunne lave en
eller anden form for typeOf:

typedef struct STRUCT1 struct1;
....

struct STRUCT1 {
char *s;
.....
}
.....

void function whatever(void **array) {
struct1 *s1 = NULL;
.....

while (*array) {
if (typeOf(array) == struct1) {
   s1 = (struct1 *) array;
}
....

if (s1) {
handle_s1(s1);
}
....
}

Er ovenstående muligt?

--
Hilsen/Regards
Michael Rasmussen
http://keyserver.veridis.com:11371/pks/lookup?op=get&search=0xE3E80917
222

Bertel Brander (23-10-2005)
Kommentar
Fra : Bertel Brander


Dato : 23-10-05 23:19

Michael Rasmussen wrote:
> Er ovenstående muligt?

Nej.
I C og C++ kendes typen ikke runtime.

I C++ har man typeid, men den kan ikke bruges til at finde
type fra en void *

I C++ kunne man lade alle structs arve fra en base class der
kunne have en måde at vide hvilken type det er.

I C er man nød til at gøre det i hånden. En metode er at lade
den første værdi i hver struct være en enum, der fortæller
hvilken type det er.

--
Absolutely not the best homepage on the net:
http://home20.inet.tele.dk/midgaard
But it's mine - Bertel

Michael Rasmussen (23-10-2005)
Kommentar
Fra : Michael Rasmussen


Dato : 23-10-05 23:34

On Mon, 24 Oct 2005 00:19:06 +0200, Bertel Brander wrote:

>
> Nej.
> I C og C++ kendes typen ikke runtime.
>
Havde jeg lidt på fornemmelsen:-\

> I C++ kunne man lade alle structs arve fra en base class der kunne have en
> måde at vide hvilken type det er.
>
Nu er det faktisk C++, jeg arbejder i, men lige i dette tilfælde synes
jeg en gammeldags C-structs er bedre - hver struct indeholder definitionen
af attributter i et ldap-schema, så det skal blot anvendes som en data
container.

> I C er man nød til at gøre det i hånden. En metode er at lade den
> første værdi i hver struct være en enum, der fortæller hvilken type
> det er.
Nu har jeg aldrig arbejdet videre med enums, så hvordan fungerer det i
praksis? Jeg ved en enum er en unik identifikation af et "objekt", så
hvis vi har en række structs:

enum SCHEMA { person, organizationalUnit, .... };

struct PERSON {
SCHEMA type = person;
....
}

struct ORGANIZATIONALUNIT {
SCHEMA type = organizationalUnit;
....
}

Hvis jeg så har en void **array med dette indhold { person,
organizationalUnit, NULL }, hvordan får jeg så dekodet og castet de
enkelte structs?

while (*array) {
/* først cast eller først læs type i struct? */
}

--
Hilsen/Regards
Michael Rasmussen
http://keyserver.veridis.com:11371/pks/lookup?op=get&search=0xE3E80917


Bertel Brander (23-10-2005)
Kommentar
Fra : Bertel Brander


Dato : 23-10-05 23:56

Michael Rasmussen wrote:
>
>>I C er man nød til at gøre det i hånden. En metode er at lade den
>>første værdi i hver struct være en enum, der fortæller hvilken type
>>det er.
>

Man kunne gøre noget i stil med nedenstående, men hvis det er C++
kan det gøres meget bedre med en fælles base class.
Jeg er ikke sikker på at nedenstående er garanteret at virke,
specielt ikke hvis nogle af struct'ene får virtuelle funktioner.

#include <iostream>

enum ObjectTypeEnum
{
AType,
BType
};

struct Base
{
ObjectTypeEnum ObjectType;
};

struct A
{
ObjectTypeEnum ObjectType;
int a;
};

struct B
{
ObjectTypeEnum ObjectType;
double b;
};

struct C
{
ObjectTypeEnum ObjectType;
const char *c;
};


void Func(void **p)
{
for(int idx = 0; p[idx]; idx++)
{
Base *base = (Base *)p[idx];
if(base->ObjectType == AType)
std::cout << ((A *)p[idx])->a << std::endl;
else if(base->ObjectType == BType)
std::cout << ((B *)p[idx])->b << std::endl;
else
std::cout << "Ups" << std::endl;
}
}

int main()
{
A a;
a.a = 123;
a.ObjectType = AType;
B b;
b.b = 321.312;
b.ObjectType = BType;

void *Array[3] = {&a, &b, 0};
Func(Array);
}

--
Absolutely not the best homepage on the net:
http://home20.inet.tele.dk/midgaard
But it's mine - Bertel

Bertel Brander (24-10-2005)
Kommentar
Fra : Bertel Brander


Dato : 24-10-05 00:10

Bertel Brander wrote:
> Michael Rasmussen wrote:
>
>>
>>> I C er man nød til at gøre det i hånden. En metode er at lade den
>>> første værdi i hver struct være en enum, der fortæller hvilken type
>>> det er.
>>
>>
>
> Man kunne gøre noget i stil med nedenstående, men hvis det er C++
> kan det gøres meget bedre med en fælles base class.

Det kunne se sådan ud:
#include <iostream>

enum ObjectTypeEnum
{
AType,
BType
};

class A;
class B;

class Base
{
protected:
Base(ObjectTypeEnum aObjectType) : ObjectType(aObjectType)
{}
public:
ObjectTypeEnum GetObjectType() const { return ObjectType; }
A *GetAsA()
{
if(ObjectType == AType)
return (A *)this;
else return 0;
}
B *GetAsB()
{
if(ObjectType == BType)
return (B *)this;
else
return 0;
}
private:
const ObjectTypeEnum ObjectType;
};

class A : public Base
{
public:
A() : Base(AType)
{}
int a;
};

struct B : public Base
{
B() : Base(BType)
{}
double b;
};

void Func(Base **p)
{
for(int idx = 0; p[idx]; idx++)
{
if(p[idx]->GetObjectType() == AType)
std::cout << p[idx]->GetAsA()->a << std::endl;
else if(p[idx]->GetObjectType() == BType)
std::cout << p[idx]->GetAsB()->b << std::endl;
else
std::cout << "Ups" << std::endl;
}
}

int main()
{
A a;
a.a = 123;
B b;
b.b = 321.312;

Base *Array[3] = {&a, &b, 0};
Func(Array);
}

Man kunne også bruge en std::vector af Base * i stedet for
base *Array[], eller man kunne lave en udskrive funktion i
Base, som en pure virtual funktion,eller man kunne lade
GetAsX funktioner kaste en exception hvis man forsøger at
hente en forkert type eller ...

--
Absolutely not the best homepage on the net:
http://home20.inet.tele.dk/midgaard
But it's mine - Bertel

Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 06:51


"Bertel Brander" <bertel@post4.tele.dk> wrote in message
news:435c1834$0$144$edfadb0f@dread11.news.tele.dk...

[8<8<8<]
> class Base
> {
> protected:
> Base(ObjectTypeEnum aObjectType) : ObjectType(aObjectType)
> {}

Hvis det er tiltænkt at være en basis-klasse, så burde destructoren sikkert
være public, virtuel, hvormed RTTI slåes til for klassen.
Så har man slet ikke brug for den type enum.

> public:
> ObjectTypeEnum GetObjectType() const { return ObjectType; }
> A *GetAsA()
> {
> if(ObjectType == AType)
> return (A *)this;
> else return 0;
> }

Det vil compileren så lægge ud så man kan bruge dynamic_cast<T*>

[8<8<8<]
> if(p[idx]->GetObjectType() == AType)
> std::cout << p[idx]->GetAsA()->a << std::endl;

Bliver så til
if(A* a = dynamic_cast<A*>(p[idx]))
std::cout << a->a << std::endl;

Venlig hilsen

Mogens Hansen



Bertel Brander (24-10-2005)
Kommentar
Fra : Bertel Brander


Dato : 24-10-05 19:07

Mogens Hansen wrote:
> "Bertel Brander" <bertel@post4.tele.dk> wrote in message
> news:435c1834$0$144$edfadb0f@dread11.news.tele.dk...
>
> [8<8<8<]
>
>>class Base
>>{
>>protected:
>> Base(ObjectTypeEnum aObjectType) : ObjectType(aObjectType)
>> {}
>
>
> Hvis det er tiltænkt at være en basis-klasse, så burde destructoren sikkert
> være public, virtuel, hvormed RTTI slåes til for klassen.
> Så har man slet ikke brug for den type enum.

Ja.

Men det forudsætter at man har råd til at tilføje en virtual func
tabel pointer til hver class og at man har råd til at slæbe rundt
på RTTI (og at man har en compiler der understøtter det).

Det er imho også en ulempe at man ikke kan spørge et object
hvilken type det er.

Mit kode var tænkt som et simplet eksempel på hvordan man
kunne gøre det i C++, uden at bruge en mange avancerede
C++ features.

--
Absolutely not the best homepage on the net:
http://home20.inet.tele.dk/midgaard
But it's mine - Bertel

Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 19:47


"Bertel Brander" <bertel@post4.tele.dk> wrote in message
news:435d22d2$0$170$edfadb0f@dread11.news.tele.dk...

[8<8<8<]
> Men det forudsætter at man har råd til at tilføje en virtual func
> tabel pointer til hver class

Det er svært at forestille sig en måde at implementere runtime polymorphy
mere effektivt.
Se eventuelt http://www.research.att.com/~bs/performanceTR.pdf

> og at man har råd til at slæbe rundt
> på RTTI (og at man har en compiler der understøtter det).

> Det er imho også en ulempe at man ikke kan spørge et object
> hvilken type det er.

Det kan man med typeid
Man kan f.eks. skrive:

#include <typeinfo>
#include <cassert>

using namespace std;

class base
{
public:
virtual ~base(); // Enable RTTI
};

class derived_1 : public base
{
};

class derived_2 : public base
{
};

class derived_3 : public derived_1
{
};

base:base()
{
}

bool is_derived_1(base& b)
{
return typeid(b) == typeid(derived_1);
}

int main()
{
derived_1 d1;
derived_2 d2;
derived_3 d3;

assert( is_derived_1(d1));
assert(!is_derived_1(d2));
assert(!is_derived_1(d2));
}


Venlig hilsen

Mogens Hansen



Ivan Johansen (24-10-2005)
Kommentar
Fra : Ivan Johansen


Dato : 24-10-05 21:44

Bertel Brander wrote:
> Men det forudsætter at man har råd til at tilføje en virtual func
> tabel pointer til hver class og at man har råd til at slæbe rundt
> på RTTI (og at man har en compiler der understøtter det).

Hvis compileren ikke understøtter det kan det næppe kaldes en C++ compiler.

> Mit kode var tænkt som et simplet eksempel på hvordan man
> kunne gøre det i C++, uden at bruge en mange avancerede
> C++ features.

Er det runtime polymorphy du kalder avanceret?

Ivan Johansen



Bertel Brander (24-10-2005)
Kommentar
Fra : Bertel Brander


Dato : 24-10-05 22:06

Ivan Johansen wrote:
> Bertel Brander wrote:
>
>> Men det forudsætter at man har råd til at tilføje en virtual func
>> tabel pointer til hver class og at man har råd til at slæbe rundt
>> på RTTI (og at man har en compiler der understøtter det).
>
>
> Hvis compileren ikke understøtter det kan det næppe kaldes en C++ compiler.

Der er stadig folk der programmerer på platforme hvor RTTI
er en for dyr utilladelig luksus. Og hvor man ikke bare laver
destructoren virtual bare fordi det er "god c++ kode stil".

Og der bruges stadig C++ kompilere der ikke har RTTI, disse er
så ikke ANSI kompatible.

>> Mit kode var tænkt som et simplet eksempel på hvordan man
>> kunne gøre det i C++, uden at bruge en mange avancerede
>> C++ features.
>
> Er det runtime polymorphy du kalder avanceret?

Jeg forsøgte at "oversætte" C eksemplet til C++ unden
at bruge flere teknikker en nødvendigt. Man kunne naturligvis
godt have lavet et mere avanceret og "kønnere" eksempel, men
jeg var bange for at pointen i eksemplet ville forsvinde i tågen.

Så vidt jeg kan se er C++ koden lige så effektiv som og en del
kønnere end C koden, så hvis OP havde valgmuligheden kunne han
lige så godt vælge den.

Det er min erfaring at hvis man vil "sælge" en C++ løsning til
en der har C baggrund gør man bedst i at levere den simpleste
løsning, der løser problemet.

--
Absolutely not the best homepage on the net:
http://home20.inet.tele.dk/midgaard
But it's mine - Bertel

Ivan Johansen (24-10-2005)
Kommentar
Fra : Ivan Johansen


Dato : 24-10-05 23:03

Bertel Brander wrote:
> Der er stadig folk der programmerer på platforme hvor RTTI
> er en for dyr utilladelig luksus. Og hvor man ikke bare laver
> destructoren virtual bare fordi det er "god c++ kode stil".

I dette tilfælde er det vidst snarer for dyrt at lade være.

> Og der bruges stadig C++ kompilere der ikke har RTTI, disse er
> så ikke ANSI kompatible.

I så fald er der vist ikke meget C++ over det. Mit indtryk er dog at vi
ikke snakker om sådan et miljø her.

> Jeg forsøgte at "oversætte" C eksemplet til C++ unden
> at bruge flere teknikker en nødvendigt.

For mig at se bruger du netop flere teknikker end nødvendigt for at
emulere runtime polymorphy i C.

> Så vidt jeg kan se er C++ koden lige så effektiv som og en del
> kønnere end C koden, så hvis OP havde valgmuligheden kunne han
> lige så godt vælge den.

Helt enig.

> Det er min erfaring at hvis man vil "sælge" en C++ løsning til
> en der har C baggrund gør man bedst i at levere den simpleste
> løsning, der løser problemet.

Jeg skal ikke kunne udtale mig om "salg" af C++-løsninger, men jeg mener
helt klart at din løsning er den mest komplicerede.

Ivan Johansen

Bertel Brander (24-10-2005)
Kommentar
Fra : Bertel Brander


Dato : 24-10-05 23:41

Ivan Johansen wrote:
>> Jeg forsøgte at "oversætte" C eksemplet til C++ unden
>> at bruge flere teknikker en nødvendigt.
>
>
> For mig at se bruger du netop flere teknikker end nødvendigt for at
> emulere runtime polymorphy i C.

Hvis det var ren runtime polymorphy, så måske.
Men hvis de objekter som man skal arbejde på ikke er
relaterede...

Så vidt jeg kan se er de enneste C++ teknikker jeg
bruger nedarvning og constructorer. Jeg tvivler på at
det kan gøres simplere.

--
Absolutely not the best homepage on the net:
http://home20.inet.tele.dk/midgaard
But it's mine - Bertel

Ivan Johansen (25-10-2005)
Kommentar
Fra : Ivan Johansen


Dato : 25-10-05 07:38

Bertel Brander wrote:
> Så vidt jeg kan se er de enneste C++ teknikker jeg
> bruger nedarvning og constructorer. Jeg tvivler på at
> det kan gøres simplere.

Du bruger også en enum til at emulere virtuelle funktioner.

Ivan Johansen



Troels Thomsen (24-10-2005)
Kommentar
Fra : Troels Thomsen


Dato : 24-10-05 09:36


Jeg har af og til set nedenstående variation.
Fordelen er at man ikke skal caste, ifald man synes dette ser "grimt" ud.
Ulempen er at unionen gør typen lige så stor som det største medlem.
( her findes så igen nogle tricks ... )

enum ObjectTypeEnum
{
AType,
BType
};

struct A
{
int a;
};

struct B
{
double b;
};

struct C
{
const char *c;
};


struct allTypes
{
ObjectTypeEnum ObjectType;
union
{
A aType;
B bType;
C cType;
};
};


void handle_aType(A obj) // evt pointer eller reference
{
std::cout << obj.a << std::endl;
}

void handle_bType(B obj) // evt pointer eller reference
{
std::cout << obj.b << std::endl;
}

void handleObj(allTypes obj)
{
switch (obj.ObjectType)
{
case (AType):
handle_aType( obj.aType );
break;
case (BType):
handle_bType( obj.bType );
break;
default:
// do something
break;
}
}

int main(int argc, char* argv[])
{
allTypes obj1;
obj1.ObjectType = AType;
obj1.aType.a = 2;

handleObj(obj1);

return 0;
}
//---------------------------------------------------------------------------





Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 18:16


"Troels Thomsen" <asdf@asdf.dk> wrote in message
news:435c9cf8$0$1810$edfadb0f@dread11.news.tele.dk...

[8<8<8<]
> Ulempen er at unionen gør typen lige så stor som det største medlem.

For mig at se er de væsentligste ulemper:
* Der er en del manuelt arbejde som kan gå galt
Arbejde som compileren egentlig er langt bedre til at håndtere
* De typer der kan indgå i en union er begrænset til POD typer

For en mere robust C++ løsning se Boost::variant
(http://www.boost.org/doc/html/variant.html)

Venlig hilsen

Mogens Hansen



Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 18:10


"Bertel Brander" <bertel@post4.tele.dk> wrote in message
news:435c150a$0$198$edfadb0f@dread11.news.tele.dk...

[8<8<8<]
> enum ObjectTypeEnum
> {
> AType,
> BType
> };

Der 2 vedligeholdelsesproblemer med den konstruktion:
* Den skal vedligeholder manuelt, og tildelingen til ObjectType skal
udføres manuelt
* Man får et globalt koblingspunkt, således at hver gang der tilføjes en
ny type skal samtlige type oversættes (ligesom den almindelige visitor
design pattern implementering)

[8<8<8<]
> Base *base = (Base *)p[idx];
> if(base->ObjectType == AType)
> std::cout << ((A *)p[idx])->a << std::endl;
> else if(base->ObjectType == BType)
> std::cout << ((B *)p[idx])->b << std::endl;
> else
> std::cout << "Ups" << std::endl;

Hvis man bruger switch bliver kørselstiden formodentlig O(1), hvor den viste
løsning nemt bliver O(n).
En lidt værre ting er at switch på typen skal dupleres og vedligeholdes alle
steder hvor den bruges.


Jeg lavede for 4 år siden noget kode som følge af et spørgsmål fra Scott
Meyers, der ved hjælp af template metaprogrammering kunne lave runtime
polymorphi under stærke begrænsninger på:
* uden brug af virtuelle metoder
* uden brug af RTTI
* uden at skulle vedligeholde en enum
* uden at frameworket afhænger af applikationskoden
* som kører _meget_ effektiv - mere effektivt end visitor patternet

Scott Meyers problem bestod i at man skulle parse noget tekst, som man så
fik en række tokens ud af. De forskellige type tokens skulle så behandles
forskelligt.

Metoden er velegnet som alternativ til visitor design patternes, hvis man
har et rimeligt statisk antal typer men et over tid variende antal
algoritmer.
Den er naturligvis også velegnet hvis man af en eller anden grund ikke vil
bruge almindelige virtuelle metoder eller RTTI.
Som udgangspunkt vil jeg dog nok almindeligvis benyttes visitor patternet i
en eller anden form - det er simplere at forstå og mere udbredt.

Koden er delt op i 5 blokke:
1. noget type_list erklæringer
2. noget external polymorphism framework
3. nogle applikations typer
4. nogle funktioner der arbejder på applikations typerne
5. et lille test program der bruger det

Det anbefales at læse blokkene i omvendt rækkefølge. Det er ikke trivielt,
men ganske interessant.

I koden står at det ikke oversætter med Microsoft Visual C++ V7.0, men det
oversætter med V7.1 (Visual Studio .NET 2003) og V8.0 (Visual Studio .NET
2005).

// File: scott_token.cpp
// Date:
//
// Example of "External Polymorphism" with
// generativ programming for automaticly creating jumptables.
// Highly optimized for execution speed.
//
// Solution to question by Scott Meyers on newsgroup
// comp.lang.c++.moderated
//
// This file has been compiled with
// Comeau C/C++ 4.2.45.2
// Borland C++Builder 5 Patch 1 on MS-Windows 2000
// Borland C++Builder 6 on MS-Windows XP
// Borland C++Builder 6 Patch 4 on MS-Windows XP
// Intel C++ 5.0.1 on MS-Windows 2000
// gcc 2.96 on Red Hat Linux 7.1
//
// but does not compile with
// Microsoft Visual C++ V6SP4 on MS-Windows 2000
// Microsoft Visual C++ V7.0 on MS-Windows 2000

#include <iostream>
#include <vector>
#include <algorithm>


#if defined(__INTEL_COMPILER) && (500 == __INTEL_COMPILER) &&
defined(_WIN32)
// Intel C++ V5.0 for MS-Windows
// Disable warnings due to debug information truncation, due to Microsoft
compatiblity
#pragma warning(disable : 4786)
#endif

#if defined(__BORLANDC__)
// Borland C++
// Disable warning: Functions containing 'statement' are not expanded inline
#pragma warn -inl
#endif

// Some typelist stuff
// Very much like
// Modern C++ Design
// Andrei Alexandrescu
// ISBN 0-201-70431
// but without preprocessor macros

class null_type;

template <class T, class U>
struct type_list_impl
{
typedef T head;
typedef U tail;
};

template <typename T1, typename T2 = null_type, typename T3 = null_type>
struct type_list;

template <typename T1, typename T2>
struct type_list<T1, T2, null_type>
{
typedef type_list_impl<T1, typename type_list<T2>::list >
list;
};

template <typename T1>
struct type_list<T1, null_type, null_type>
{
typedef type_list_impl<T1, null_type>
list;
};

//external polymorphic framework

template <typename T>
class external_polymorphic
{
public:
unsigned id() const;
static unsigned max_class_id();

protected:
external_polymorphic(unsigned id);

static unsigned register_class();

private:
static unsigned& class_count();

private:
const unsigned id_;
};

template <typename T, typename D>
class reg_external_polymorphic : public T
{
public:
static unsigned class_id();

protected:
reg_external_polymorphic();

template <typename ARG1>
reg_external_polymorphic(ARG1 arg1);

template <typename ARG1, typename ARG2>
reg_external_polymorphic(ARG1 arg1, ARG2 arg2);

template <typename ARG1, typename ARG2, typename ARG3>
reg_external_polymorphic(ARG1 arg1, ARG2 arg2, ARG3 arg3);

template <typename ARG1, typename ARG2, typename ARG3, typename ARG4>
reg_external_polymorphic(ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4);

~reg_external_polymorphic();

private:
// ensure that class_id is called, at latest before main is entered
// so that all classes are registered
static const unsigned class_id_;
};

template <typename T, typename D>
const unsigned reg_external_polymorphic<T,D>::class_id_ =
reg_external_polymorphic<T,D>::class_id();

template <typename T>
inline external_polymorphic<T>::external_polymorphic(unsigned id) :
id_(id)
{
T* t = static_cast<T*>(this);
t; // used
}

template <typename T>
inline unsigned external_polymorphic<T>::id() const
{
return id_;
}

template <typename T>
inline unsigned external_polymorphic<T>::max_class_id()
{
return class_count();
}

template <typename T>
inline unsigned& external_polymorphic<T>::class_count()
{
static unsigned class_count_ = 0;
return class_count_;
}

// made non-inline due to bug in C++Builder V6.0 code generation
template <typename T>
#if defined(__BORLANDC__)
#else
inline
#endif
unsigned external_polymorphic<T>::register_class()
{
return class_count()++;
}

template <typename T, typename D>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic() :
T(class_id())
{
}

template <typename T, typename D>
template <typename ARG1>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic(ARG1 arg1) :
T(class_id(), arg1)
{
}

template <typename T, typename D>
template <typename ARG1, typename ARG2>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic(ARG1 arg1,
ARG2 arg2) :
T(class_id(), arg1, arg2)
{
}

template <typename T, typename D>
template <typename ARG1, typename ARG2, typename ARG3>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic(ARG1 arg1,
ARG2 arg2, ARG3 arg3) :
T(class_id(), arg1, arg2, arg3)
{
}

template <typename T, typename D>
template <typename ARG1, typename ARG2, typename ARG3, typename ARG4>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic(ARG1 arg1,
ARG2 arg2, ARG3 arg3, ARG4 arg4) :
T(class_id(), arg1, arg2, arg3, arg4)
{
}

template <typename T, typename D>
inline reg_external_polymorphic<T, D>:reg_external_polymorphic()
{
// D must be derived from this class
reg_external_polymorphic<T, D>* d = static_cast<D*>(0);
d; //used!

// This class must be derived from external_polymorphic<T>
external_polymorphic<T>* ept = this;
ept; //used!!

&class_id_; // ensure class_id_ is reference and not optimized away !
}

template <typename T, typename D>
inline unsigned reg_external_polymorphic<T, D>::class_id()
{
static const unsigned class_id_ = register_class();
return class_id_;
}

// The token framework

class token : public external_polymorphic<token>
{
public:
// the destructor _need_ not be virtual
// we do not rely on RTTI
// virtual ~token() {}

protected:
// destructor made protected to prevent
token(unsigned id) :
external_polymorphic<token>(id) {}

private:
// copy constructor and copy assignment not implemented
token(const token&);
token& operator=(const token&);
};

// "external polymorphism"
// see Bjarne Stroustrup slides
// "Design Decisions for a Library"
// http://www.klid.dk/arrangementer/XTI_kbh.pdf
// slide 14-18
//
// this vcall template class is modelled after those slides

// helper classes for vcall

template <typename T1>
struct do_static_cast
{
template <typename T2>
T2 do_cast(T1 t)
{ return static_cast<T2>(t); }
};

template <typename T1>
struct do_dynamic_cast
{
template <typename T2>
T2 do_cast(T1 t)
{ return dynamic_cast<T2>(t); }
};

template <typename Func, typename DispatchTypelist, typename CastPolicy =
do_static_cast<const token&> >
struct func_table_generator
{
typedef typename Func::return_type return_type;
typedef return_type (*func_ptr_type)(const token&);
typedef std::vector<func_ptr_type> func_vector_type;

static void fill_func_vector(func_vector_type& func_vector)
{ func_table_generator<Func, typename
DispatchTypelist::list>::fill_func_vector(func_vector); }

static unsigned max_class_id()
{ return func_table_generator<Func, typename
DispatchTypelist::list>::max_class_id(); }
};

template <typename Func, typename T, typename CastPolicy>
struct func_table_generator<Func, type_list_impl<T, null_type>, CastPolicy>
{
typedef typename Func::return_type return_type;
typedef return_type (*func_ptr_type)(const token&);
typedef std::vector<func_ptr_type> func_vector_type;

static return_type do_call(const token& tok)
{
return Func()(CastPolicy().do_cast<const T&>(tok));
}

static void fill_func_vector(func_vector_type& func_vector)
{
func_vector[T::class_id()] = do_call;
}

static unsigned max_class_id()
{
return T::max_class_id();
}
};

template <typename Func, typename T, typename U, typename CastPolicy>
struct func_table_generator<Func, type_list_impl<T, U>, CastPolicy>
{
typedef typename Func::return_type return_type;
typedef return_type (*func_ptr_type)(const token&);
typedef std::vector<func_ptr_type> func_vector_type;

static return_type do_call(const token& tok)
{
return Func()(CastPolicy().do_cast<const T&>(tok));
}

static void fill_func_vector(func_vector_type& func_vector)
{
func_vector[T::class_id()] = do_call;
func_table_generator<Func, typename U>::fill_func_vector(func_vector);
}

static unsigned max_class_id()
{
return T::max_class_id();
}
};

template <typename ReturnType>
struct pure_virtual_throw
{
static ReturnType pure_virtual()
{ throw 0; /* or something more sensible */ }
template <typename ARG0>
static ReturnType pure_virtual(ARG0 /*arg0*/)
{ throw 0; /* or something more sensible */ }
template <typename ARG0, typename ARG1>
static ReturnType pure_virtual(ARG0 /*arg0*/, ARG1 /*arg1*/)
{ throw 0; /* or something more sensible */ }
template <typename ARG0, typename ARG1, typename ARG2>
static ReturnType pure_virtual(ARG0 /*arg0*/, ARG1 /*arg1*/, ARG2
/*arg2*/)
{ throw 0; /* or something more sensible */ }
template <typename ARG0, typename ARG1, typename ARG2, typename ARG3>
static ReturnType pure_virtual(ARG0 /*arg0*/, ARG1 /*arg1*/, ARG3
/*arg3*/)
{ throw 0; /* or something more sensible */ }
};

template <typename ReturnType>
struct pure_virtual_nothing
{
static ReturnType pure_virtual()
{ }
template <typename ARG0>
static ReturnType pure_virtual(ARG0 /*arg0*/)
{ }
template <typename ARG0, typename ARG1>
static ReturnType pure_virtual(ARG0 /*arg0*/, ARG1 /*arg1*/)
{ }
template <typename ARG0, typename ARG1, typename ARG2>
static ReturnType pure_virtual(ARG0 /*arg0*/, ARG1 /*arg1*/, ARG2
/*arg2*/)
{ }
template <typename ARG0, typename ARG1, typename ARG2, typename ARG3>
static ReturnType pure_virtual(ARG0 /*arg0*/, ARG1 /*arg1*/, ARG3
/*arg3*/)
{ }
};

template <typename Func, typename DispatchTypelist, typename
PureVirtualPolicy = pure_virtual_throw<typename Func::return_type> >
struct func_table
{
typedef typename Func::return_type return_type;
typedef return_type (*func_ptr_type)(const token&);
typedef std::vector<func_ptr_type> func_vector_type;

func_table();

const func_vector_type& func_vector() const
{ return func_vector_; }

static return_type pure_virtual(const token& tok)
{ PureVirtualPolicy::pure_virtual<const token&>(tok); }

private:
func_vector_type func_vector_;

private:
func_table(const func_table&);
func_table& operator=(const func_table&);
};

// constructor is not inlined - only called once
// code bloat if inlined
template <typename Func, typename DispatchTypelist, typename
PureVirtualPolicy>
func_table<Func, DispatchTypelist, PureVirtualPolicy>::func_table() :
func_vector_(func_table_generator<Func,
DispatchTypelist>::max_class_id()+1, pure_virtual)
{
func_table_generator<Func,
DispatchTypelist>::fill_func_vector(func_vector_);
}

template <typename DispatchTypelist, typename Func>
inline typename Func::return_type vcall(const token& tok)
{
typedef func_table<Func, DispatchTypelist>
func_table_type;
typedef typename func_table_type::func_vector_type
func_vector_type;

// jump-table initialized first time vcall is called
static const func_table_type func_tbl;

// Check for "tok.id() >= func_tbl.func_vector()" not needed
// because all classes are registered before main is entered
// Does not work if classes are loaded dynamic -
// like with .DLL or .so files
return (*func_tbl.func_vector()[tok.id()])(tok);
}

// application code

class special_token_1 : public reg_external_polymorphic<token,
special_token_1>
{
public:
special_token_1() {}
};

class special_token_2 : public reg_external_polymorphic<token,
special_token_2>
{
public:
special_token_2() {}
};

class special_token_3 : public reg_external_polymorphic<token,
special_token_3>
{
public:
special_token_3() {}
};



struct func_process
{
public:
typedef void return_type;

template <class T>
return_type operator()(const T& t)
{ return process(t); }
};

inline void process(const token& tok)
{
typedef type_list<
special_token_1,
special_token_2>
token_type_list;
return vcall<token_type_list, func_process>(tok);
}

template <class T>
void process(const T&); // not implemented - catch non exact type match a
link time

inline void process(const special_token_1& tok)
{
using std::cout;
using std::endl;

cout << "processing special_token_1" << endl;
}

inline void process(const special_token_2& tok)
{
using std::cout;
using std::endl;

cout << "processing special_token_2" << endl;
}

int main(int argc, char* argv[])
{
using std::cerr; using std::cout;
using std::endl;

special_token_1 st1;
special_token_2 st2;
special_token_3 st3;

token* tok = &st1;
process(*tok);

tok = &st2;
process(*tok);

tok = &st3;
try {
process(*tok);
cerr << "Error: unknown token type not detected!!!" << endl;
}
catch(...) {
cout << "special_token_3 is correctly not known :)" << endl;
}
}


Venlig hilsen

Mogens Hansen



Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 12:02


"Michael Rasmussen" <mir@miras.org> wrote in message
news:pan.2005.10.23.22.33.38.660266@miras.org...
> On Mon, 24 Oct 2005 00:19:06 +0200, Bertel Brander wrote:
>
>>
>> Nej.
>> I C og C++ kendes typen ikke runtime.
>>
> Havde jeg lidt på fornemmelsen:-\
>
>> I C++ kunne man lade alle structs arve fra en base class der kunne have
>> en
>> måde at vide hvilken type det er.
>>
> Nu er det faktisk C++, jeg arbejder i,

Ok

> men lige i dette tilfælde synes
> jeg en gammeldags C-structs er bedre

Hvorfor ?
Det kæmper, så vidt jeg kan forstå, med et runtime polymorphi problem som er
direkte løst i C++ ved hjælp af basis klasser og virtuelle metoder.

Hvis antallet af typer er konstant over programmets levetid men antallet af
ting man ønsker at gøre med dem må forventes at vokse, så er man bedst tjent
med at benytte Visitor design patternet under en eller anden form.

> - hver struct indeholder definitionen
> af attributter i et ldap-schema, så det skal blot anvendes som en data
> container.

Ok.
Der er ingen modstrid mellem og brugen af runtime polymorphi i C++

[8<8<8<]
> Nu har jeg aldrig arbejdet videre med enums, så hvordan fungerer det i
> praksis? Jeg ved en enum er en unik identifikation af et "objekt", så
> hvis vi har en række structs:
>
> enum SCHEMA { person, organizationalUnit, .... };
>
> struct PERSON {
> SCHEMA type = person;
> ....
> }
>
> struct ORGANIZATIONALUNIT {
> SCHEMA type = organizationalUnit;
> ....
> }
>
> Hvis jeg så har en void **array med dette indhold { person,
> organizationalUnit, NULL }, hvordan får jeg så dekodet og castet de
> enkelte structs?

Du er nok bedst tjent med ikke at bruge sådan en enum, men at udtrykke det i
basisklasser i stedet:
Enums skalerer dårligt både vedligeholdelsesmæssigt.

class schema_visitor;

class schema
{
public:
virtual ~schema();

virtual void do_something() const = 0;
virtual void accept(schema_visitor& visitor) = 0;

private:
schema(const schema&);
schema& operator=(const schema&);
};

class person : public schema
{
public:
virtual void do_something() const;
virtual void accept(schema_visitor& visitor)
{ visitor.visit(*this);
};

class organization_unit : public schema
{
public:
virtual void do_something() const;
virtual void accept(schema_visitor& visitor)
{ visitor.visit(*this);
};

class schema_visitor
{
public:
virtual ~schema_visitor();

virtual void visit(person& person_arg) = 0;
virtual void visit(organization_unit& organization_unit_arg) = 0;

protected:
schema_visitor();

private:
schema_visitor(const schema_visitor&)
schema_visitor& operator=(const schema_visitor&);
};

>
> while (*array) {
> /* først cast eller først læs type i struct? */
> }

void foo(const vector<schema*>& schemas)
{
for(vector<schema*>::const_iterator i = schemas.begin(); schemas.end() !=
i; ++i) {
const schema& s = **i;
s.do_something();
}
}

eller hvis man bruger visitor (fordi det forventes at der løbende vil komme
mange algoritmer til)

class do_something_visitor : publi schema_visitor
{
public:
virtual void visit(person& person_arg);
virtual void visit(organization_unit& organization_unit_arg);
};

void foo(const vector<schema*>& schames)
{
do_something_visitor dsv;
for(vector<schema*>::const_iterator i = schemas.begin(); schemas.end() !=
i; ++i) {
const schema& s = **i;
s.accept(dsv);
}
}



Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 18:10


"Michael Rasmussen" <mir@miras.org> wrote in message
news:pan.2005.10.23.22.33.38.660266@miras.org...
> On Mon, 24 Oct 2005 00:19:06 +0200, Bertel Brander wrote:

[8<8<8<]
> Nu er det faktisk C++, jeg arbejder i,

Ok

> men lige i dette tilfælde synes
> jeg en gammeldags C-structs er bedre

Hvorfor ?
Du kæmper, så vidt jeg kan se, med et runtime polymorphi problem som er
direkte løst i C++ ved hjælp af basis klasser og virtuelle metoder.

Hvis antallet af typer er konstant over programmets levetid men antallet af
ting man ønsker at gøre med dem må forventes at vokse, så er man bedst tjent
med at benytte Visitor design patternet under en eller anden form.

> - hver struct indeholder definitionen
> af attributter i et ldap-schema, så det skal blot anvendes som en data
> container.

Ok.
Der er ingen modstrid mellem at bruge en klasse som data container og brugen
af runtime polymorphi i C++.

[8<8<8<]
> Nu har jeg aldrig arbejdet videre med enums, så hvordan fungerer det i
> praksis? Jeg ved en enum er en unik identifikation af et "objekt", så
> hvis vi har en række structs:
>
> enum SCHEMA { person, organizationalUnit, .... };
>
> struct PERSON {
> SCHEMA type = person;
> ....
> }
>
> struct ORGANIZATIONALUNIT {
> SCHEMA type = organizationalUnit;
> ....
> }
>
> Hvis jeg så har en void **array med dette indhold { person,
> organizationalUnit, NULL }, hvordan får jeg så dekodet og castet de
> enkelte structs?

Du er nok bedst tjent med ikke at bruge sådan en enum, men at udtrykke det i
basisklasser i stedet:
Enums brugt på den måde skalerer dårligt vedligeholdelsesmæssigt.

Prøv i stedet noget i retningen af (ikke oversat kode):

class schema_visitor;

class schema
{
public:
virtual ~schema();

virtual void do_something() const = 0;
virtual void accept(schema_visitor& visitor) = 0;

private:
schema(const schema&);
schema& operator=(const schema&);
};

class person : public schema
{
public:
virtual void do_something() const;
virtual void accept(schema_visitor& visitor)
{ visitor.visit(*this);
};

class organization_unit : public schema
{
public:
virtual void do_something() const;
virtual void accept(schema_visitor& visitor)
{ visitor.visit(*this);
};

class schema_visitor
{
public:
virtual ~schema_visitor();

virtual void visit(person& person_arg) = 0;
virtual void visit(organization_unit& organization_unit_arg) = 0;

protected:
schema_visitor();

private:
schema_visitor(const schema_visitor&)
schema_visitor& operator=(const schema_visitor&);
};

>
> while (*array) {
> /* først cast eller først læs type i struct? */
> }

void foo(const vector<schema*>& schemas)
{
for(vector<schema*>::const_iterator i = schemas.begin(); schemas.end() !=
i; ++i) {
const schema& s = **i;
s.do_something();
}
}

eller hvis man bruger visitor (fordi det forventes at der løbende vil komme
mange algoritmer til)

class do_something_visitor : publi schema_visitor
{
public:
virtual void visit(person& person_arg);
virtual void visit(organization_unit& organization_unit_arg);
};

void foo(const vector<schema*>& schames)
{
do_something_visitor dsv;
for(vector<schema*>::const_iterator i = schemas.begin(); schemas.end() !=
i; ++i) {
const schema& s = **i;
s.accept(dsv);
}
}

Venlig hilsen

Mogens Hansen



Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 18:10


"Michael Rasmussen" <mir@miras.org> wrote in message
news:pan.2005.10.23.22.33.38.660266@miras.org...
> On Mon, 24 Oct 2005 00:19:06 +0200, Bertel Brander wrote:

[8<8<8<]
> Nu er det faktisk C++, jeg arbejder i,

Ok

> men lige i dette tilfælde synes
> jeg en gammeldags C-structs er bedre

Hvorfor ?
Du kæmper, så vidt jeg kan se, med et runtime polymorphi problem som er
direkte løst i C++ ved hjælp af basis klasser og virtuelle metoder.

Hvis antallet af typer er konstant over programmets levetid men antallet af
ting man ønsker at gøre med dem må forventes at vokse, så er man bedst tjent
med at benytte Visitor design patternet under en eller anden form.

> - hver struct indeholder definitionen
> af attributter i et ldap-schema, så det skal blot anvendes som en data
> container.

Ok.
Der er ingen modstrid mellem at bruge en klasse som data container og brugen
af runtime polymorphi i C++.

[8<8<8<]
> Nu har jeg aldrig arbejdet videre med enums, så hvordan fungerer det i
> praksis? Jeg ved en enum er en unik identifikation af et "objekt", så
> hvis vi har en række structs:
>
> enum SCHEMA { person, organizationalUnit, .... };
>
> struct PERSON {
> SCHEMA type = person;
> ....
> }
>
> struct ORGANIZATIONALUNIT {
> SCHEMA type = organizationalUnit;
> ....
> }
>
> Hvis jeg så har en void **array med dette indhold { person,
> organizationalUnit, NULL }, hvordan får jeg så dekodet og castet de
> enkelte structs?

Du er nok bedst tjent med ikke at bruge sådan en enum, men at udtrykke det i
basisklasser i stedet:
Enums brugt på den måde skalerer dårligt vedligeholdelsesmæssigt.

Prøv i stedet noget i retningen af (ikke oversat kode):

class schema_visitor;

class schema
{
public:
virtual ~schema();

virtual void do_something() const = 0;
virtual void accept(schema_visitor& visitor) = 0;

private:
schema(const schema&);
schema& operator=(const schema&);
};

class person : public schema
{
public:
virtual void do_something() const;
virtual void accept(schema_visitor& visitor)
{ visitor.visit(*this);
};

class organization_unit : public schema
{
public:
virtual void do_something() const;
virtual void accept(schema_visitor& visitor)
{ visitor.visit(*this);
};

class schema_visitor
{
public:
virtual ~schema_visitor();

virtual void visit(person& person_arg) = 0;
virtual void visit(organization_unit& organization_unit_arg) = 0;

protected:
schema_visitor();

private:
schema_visitor(const schema_visitor&)
schema_visitor& operator=(const schema_visitor&);
};

>
> while (*array) {
> /* først cast eller først læs type i struct? */
> }

void foo(const vector<schema*>& schemas)
{
for(vector<schema*>::const_iterator i = schemas.begin(); schemas.end() !=
i; ++i) {
const schema& s = **i;
s.do_something();
}
}

eller hvis man bruger visitor (fordi det forventes at der løbende vil komme
mange algoritmer til)

class do_something_visitor : publi schema_visitor
{
public:
virtual void visit(person& person_arg);
virtual void visit(organization_unit& organization_unit_arg);
};

void foo(const vector<schema*>& schames)
{
do_something_visitor dsv;
for(vector<schema*>::const_iterator i = schemas.begin(); schemas.end() !=
i; ++i) {
const schema& s = **i;
s.accept(dsv);
}
}

Venlig hilsen

Mogens Hansen



Kent Friis (24-10-2005)
Kommentar
Fra : Kent Friis


Dato : 24-10-05 17:28

Den Sun, 23 Oct 2005 23:36:02 +0200 skrev Michael Rasmussen:
> On Sun, 23 Oct 2005 20:33:53 +0000, Kent Friis wrote:
>
> Hvad jeg i virkeligheden kunne ønske mig var, hvis man kunne lave en
> eller anden form for typeOf:

Jamen så var mit første svar jo alligevel det du var ude efter:

>>>C gør ikke arbejdet for dig.

Mvh
Kent
--
Hard work may pay off in the long run, but laziness pays off right now.

Igor V. Rafienko (24-10-2005)
Kommentar
Fra : Igor V. Rafienko


Dato : 24-10-05 00:17

[ Michael Rasmussen ]

[ ... ]

> 1) Er dette muligt?
> 2) Hvordan gøres det i praksis?
> 3) Hvordan kan jeg vide, hvilke structs array indeholder?


For å svare på disse spørsmålene ville det ha hjulpet å vite hva det
var du egentlig skulle gjøre. Løsninger kan nemlig variere fra det som
er foreslått, til hvordan qsort oppfører seg mtp. typene.





ivr
--
"...but it's HDTV -- it's got a better resolution than the real world."
       -- Fry, "When aliens attack"

Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 18:10


"Michael Rasmussen" <mir@miras.org> wrote in message
news:pan.2005.10.23.18.58.22.561705@miras.org...

[8<8<8<]
> Det skal forstås sådan, at jeg vil
> kunne modtage 1..n af disse structs, overført som et array af void *
> pointere, og i min funktion skal jeg så kunne caste disse til deres
> oprindelige type.

Er det dit valg at det gemmes som void* eller er det et udefra påtrykt krav
?

[8<8<8<]
> 1) Er dette muligt?

ja

> 2) Hvordan gøres det i praksis?

Der er flere muligheder
Det kommer an på hvilke øvrige krav der er til løsningen - herunder vil
antallet af type eller antallet af algoritmer være mest konstant over tid og
hvilket sprog der skal bruges

> 3) Hvordan kan jeg vide, hvilke structs array indeholder?

Hvis man smider al typeinformation væk ved at cast til void*, så har man
selv opgaven med at genskabe den manuelt.

Venlig hilsen

Mogens Hansen




Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 07:06


"Michael Rasmussen" <mir@miras.org> wrote in message
news:pan.2005.10.23.18.58.22.561705@miras.org...

[8<8<8<]
> Det skal forstås sådan, at jeg vil
> kunne modtage 1..n af disse structs, overført som et array af void *
> pointere, og i min funktion skal jeg så kunne caste disse til deres
> oprindelige type.

Er det dit valg at det gemmes som void* eller er det et udefra påtrykt krav
?

[8<8<8<]
> 1) Er dette muligt?

ja

> 2) Hvordan gøres det i praksis?

Der er flere muligheder
Det kommer an på hvilke øvrige krav der er til løsningen - herunder vil
antallet af type eller antallet af algoritmer være mest konstant over tid og
hvilket sprog der skal bruges

> 3) Hvordan kan jeg vide, hvilke structs array indeholder?

Hvis man smider al typeinformation væk ved at cast til void*, så har man
selv opgaven med at genskabe den manuelt.

Venlig hilsen

Mogens Hansen



Mogens Hansen (24-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 24-10-05 18:10


"Michael Rasmussen" <mir@miras.org> wrote in message
news:pan.2005.10.23.18.58.22.561705@miras.org...

[8<8<8<]
> Det skal forstås sådan, at jeg vil
> kunne modtage 1..n af disse structs, overført som et array af void *
> pointere, og i min funktion skal jeg så kunne caste disse til deres
> oprindelige type.

Er det dit valg at det gemmes som void* eller er det et udefra påtrykt krav
?

[8<8<8<]
> 1) Er dette muligt?

ja

> 2) Hvordan gøres det i praksis?

Der er flere muligheder
Det kommer an på hvilke øvrige krav der er til løsningen - herunder vil
antallet af type eller antallet af algoritmer være mest konstant over tid og
hvilket sprog der skal bruges

> 3) Hvordan kan jeg vide, hvilke structs array indeholder?

Hvis man smider al typeinformation væk ved at cast til void*, så har man
selv opgaven med at genskabe den manuelt.

Venlig hilsen

Mogens Hansen




Per Abrahamsen (25-10-2005)
Kommentar
Fra : Per Abrahamsen


Dato : 25-10-05 09:33

Bertel Brander <bertel@post4.tele.dk> writes:

> Der er stadig folk der programmerer på platforme hvor RTTI
> er en for dyr utilladelig luksus.

Det er RTTI uanset om du implementerer der ved håndkraft, med en
"objekt-type-enum", eller beder compileren om at gøre det ved at gøre
destructoren virtuel.

Spørgsmålet er så hvem der er de dygtigste til at implementere RTTI
med minimalt overhead, dig eller de folk der har skrevet compileren.

De kære folk der skriv er compilere har lavet undersøgelser, der viser
at de selv er de dygtigste. Eller sagt på en anden måde, at
compilerne genererer mere effektiv kode hvis man lader compileren
generere RTTI, end forsøger at implementere det med håndkraft.

Men nu er compiler-skibenter jo heller ikke neutrale, så det er muligt
at de er bedre. Men i dit sted ville jeg først *måle* at min egen
RTTI kode var bedre, i stedet for bare at antage at compilerens RTTI
er "en dyre utilladelig luksus".

Det *kunne* jo være at comiler-skriventerne har ret, og de har rent
faktisk målt på det.

N/A (26-10-2005)
Kommentar
Fra : N/A


Dato : 26-10-05 20:51



N/A (26-10-2005)
Kommentar
Fra : N/A


Dato : 26-10-05 20:51



N/A (26-10-2005)
Kommentar
Fra : N/A


Dato : 26-10-05 20:51



N/A (26-10-2005)
Kommentar
Fra : N/A


Dato : 26-10-05 20:51



Michael Rasmussen (26-10-2005)
Kommentar
Fra : Michael Rasmussen


Dato : 26-10-05 20:51

On Wed, 26 Oct 2005 20:23:04 +0200, Mogens Hansen wrote:

>
> Jeg håber du er tilfreds med Arne Vajhøj's henvisning - ellers sig til.
>
En teoretisk artikel med eksempler kan hentes her:
http://jerry.cs.uiuc.edu/~plop/plop2001/accepted_submissions/PLoP2001/ymai0/PLoP2001_ymai0_1.pdf

Et par artikler af John Vlissides - den sidste er på anbefaling af Scott
Meyers:
http://www.research.ibm.com/designpatterns/pubs/ph-nov-dec99.pdf
http://www.research.ibm.com/designpatterns/pubs/trouble.html

--
Hilsen/Regards
Michael Rasmussen
http://keyserver.veridis.com:11371/pks/lookup?op=get&search=0xE3E80917


Michael Rasmussen (25-10-2005)
Kommentar
Fra : Michael Rasmussen


Dato : 25-10-05 12:28

On Sun, 23 Oct 2005 20:58:23 +0200, Michael Rasmussen wrote:

En nærmere skitsering af problematikken var ønsket, så det vil forsøge
på her:

Jeg er i gang med at udvikle en applikation, der skal kunne konfigurere og
administrere en directory server (DS) til at styre brugere og deres adgang
til forskellige resourcer - en minimalistisk AD.

En DS er defineret ud fra en række skemaer, som vi i en OO verden kan
kalde basisklasser. Disse basisklasser antages at være konstante over
tid, hvorfor objectstrukturen må antages at være konstant. Hvert skema
beskriver en række attributter, hvoraf nogen er obligatoriske, mens andre
er valgfrie. Obligatorisk betyder i denne sammenhæng, at hvis et objekt
arver fra dette basisskema, skal de obligatoriske attributter være
defineret i subklassen og have en værdi - NULL og tom er ikke en værdi.
Valgfrie attributter behøver ikke at være defineret i subklassen, men er
de defineret, skal de have en værdi.

En DS lagre alle oplysninger i en træstruktur, hvor hver enkelt node er
defineret ud fra en eller flere basisklasser, og nøglen til den enkelte
node kaldes dn. Nøglen er opbygget som en tekststreng, hvor hvert led er
adskilt af et komma. Hvert led er også samtidigt navnet på parent.

Efter denne meget summariske beskrivelse af domænet, går jeg nu over til
den konkrete problemstilling.

For at håndtere DS, er jeg i gang med at konstruere en klasse, der
definere alle lovlige operationer på DS samt en beskrivelse af samtlige
basisklasser. Indtil videre har jeg beskrevet basisklasserne i en struct,
og oprettet en pointer i klassen til samtlige basisklasser. Tager vi
f.eks. den lovlige operation add, ser forskriften ud på følgende måde:
int ldap_add_s(LDAP *ld, const char *dn, LDAPMod *attrs[]). ld er en
refence til en DS, dn er nøglen for en ny node, og attrs[] er en refence
til en liste over basisklasser samt attributter, denne node skal oprettes
med. En LDAPMod er en struct defineret i ldap.h, og har følgende
forskrift:
typedef struct ldapmod {
int mod_op;
char *mod_type;
union {
char **modv_strvals;
struct berval **modv_bvals;
} mod_vals;
struct ldapmod *mod_next;
} LDAPMod;
#define mod_values mod_vals.modv_strvals #define mod_bvalues
mod_vals.modv_bvals

The mod_op field is used to specify the type of modification to
perform and should be one of LDAP_MOD_ADD,
LDAP_MOD_DELETE, or LDAP_MOD_REPLACE. The mod_type and
mod_values fields specify the attribute type to modify and a
null-terminated array of values to add, delete, or replace
respectively. The mod_next field is used only by the LDAP server
and may be ignored by the client.

Min metoden har denne foreskrift: bool addEntry(char *dn, void
**objectClass, map<string, string> attr). dn er nøglen for den nye node,
objectClass er en reference til en liste af basisklasser, og attr er et
map med attributter og værdier, denne node skal oprettes med. Tanken er
så, at jeg vil teste, hvorvidt samtlige obligatoriske attributter, der
findes i listen af basisklasser, også er defineret i attr, og om disse
har en værdi, inden jeg kalder ldap_add_s.

Håber dette giver en mere præcis beskrivelse af min problemstilling, og
mine ønsker til løsningen. Det skal understreges, at mine problemer kun
relatere sig til løsningen af void **objectClass problematikken.

Til Mogens: Jeg har studeret dit forslag nærmere, og antager du basere
det på GOF-mønstret Visitor(Design Patterns:331-344). Læser vi nu
anvendelsesområdet for Visitor (Applicability), synes jeg ikke lige, det
passer på min problemstilling. 1) En objektstruktur med mange klasser af
objekter ... - passer ikke her 2) Mange specifikke og urelaterede
operationer ... - passer ikke her 3) Klassen der definere objektstrukturen
antages at være en tilnærmet konstant, men man ønsker at definere nye
operationer på strukturen. Først led passer her, men sidste led gør
ikke, da operationerne på strukturen også er konstante.

Din løsning ville dog sagtens kunne implementeres, men får jeg gavn af
mønstret i den her sammenhæng?

--
Hilsen/Regards
Michael Rasmussen
http://keyserver.veridis.com:11371/pks/lookup?op=get&search=0xE3E80917


Mogens Hansen (25-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 25-10-05 18:03


"Michael Rasmussen" <mir@miras.org> wrote in message
news:pan.2005.10.25.11.27.34.830188@miras.org...
> On Sun, 23 Oct 2005 20:58:23 +0200, Michael Rasmussen wrote:
>
> En nærmere skitsering af problematikken var ønsket, så det vil forsøge
> på her:
>
> Jeg er i gang med at udvikle en applikation, der skal kunne konfigurere og
> administrere en directory server (DS) til at styre brugere og deres adgang
> til forskellige resourcer - en minimalistisk AD.
>
> En DS er defineret ud fra en række skemaer, som vi i en OO verden kan
> kalde basisklasser. Disse basisklasser antages at være konstante over
> tid, hvorfor objectstrukturen må antages at være konstant. Hvert skema
> beskriver en række attributter, hvoraf nogen er obligatoriske, mens andre
> er valgfrie. Obligatorisk betyder i denne sammenhæng, at hvis et objekt
> arver fra dette basisskema, skal de obligatoriske attributter være
> defineret i subklassen og have en værdi - NULL og tom er ikke en værdi.
> Valgfrie attributter behøver ikke at være defineret i subklassen, men er
> de defineret, skal de have en værdi.

Jeg er ikke helt sikker på at jeg forstår dig ret, men vær forsigtig med at
putte atributter i (abstrakte) basisklasser.
Definer det interface som brugerne (applikations programmøren og designeren
af afledte klasser) skal bruge.
Tænk i commonality og variability når man bestemmer hvad der skal være i de
forskellige klasser.

>
> En DS lagre alle oplysninger i en træstruktur, hvor hver enkelt node er
> defineret ud fra en eller flere basisklasser, og nøglen til den enkelte
> node kaldes dn. Nøglen er opbygget som en tekststreng, hvor hvert led er
> adskilt af et komma. Hvert led er også samtidigt navnet på parent.
>
> Efter denne meget summariske beskrivelse af domænet, går jeg nu over til
> den konkrete problemstilling.
>
> For at håndtere DS, er jeg i gang med at konstruere en klasse, der
> definere alle lovlige operationer på DS samt en beskrivelse af samtlige
> basisklasser. Indtil videre har jeg beskrevet basisklasserne i en struct,
> og oprettet en pointer i klassen til samtlige basisklasser. Tager vi
> f.eks. den lovlige operation add, ser forskriften ud på følgende måde:
> int ldap_add_s(LDAP *ld, const char *dn, LDAPMod *attrs[]). ld er en
> refence til en DS, dn er nøglen for en ny node, og attrs[] er en refence
> til en liste over basisklasser samt attributter, denne node skal oprettes
> med. En LDAPMod er en struct defineret i ldap.h, og har følgende
> forskrift:
> typedef struct ldapmod {
> int mod_op;
> char *mod_type;
> union {
> char **modv_strvals;
> struct berval **modv_bvals;
> } mod_vals;
> struct ldapmod *mod_next;
> } LDAPMod;
> #define mod_values mod_vals.modv_strvals #define mod_bvalues
> mod_vals.modv_bvals
>
> The mod_op field is used to specify the type of modification to
> perform and should be one of LDAP_MOD_ADD,
> LDAP_MOD_DELETE, or LDAP_MOD_REPLACE. The mod_type and
> mod_values fields specify the attribute type to modify and a
> null-terminated array of values to add, delete, or replace
> respectively. The mod_next field is used only by the LDAP server
> and may be ignored by the client.
>
> Min metoden har denne foreskrift: bool addEntry(char *dn, void
> **objectClass, map<string, string> attr). dn er nøglen for den nye node,
> objectClass er en reference til en liste af basisklasser,

Jeg savner stadig fuldstændigt at se hvorfor det skal være void*, og pointer
til pointer i stedet for noget mere typesikkert som f.eks.
bool addEntry(..., const vector<schema*>& ...)
eller måske endda implementere det via et handle-body idiom.
Der er et skønt eksempel på håndtering af træstruktur på den måde i
Accelerated C++
Andrew Koenig, Barbara Moo
ISBN 0-201-70353-X
Sourcekoden til programmerne kan iøvrigt downloades fra
www.acceleratedcpp.com

> og attr er et
> map med attributter og værdier, denne node skal oprettes med.

Almindeligvis kan det betale sig at overføre en kompliceret struktur som
map<string, string> by reference i stedet for by value - på den måde slipper
man for at lave en kopi:
bool addEntry(...., const map<string, string>&...)

> Tanken er
> så, at jeg vil teste, hvorvidt samtlige obligatoriske attributter, der
> findes i listen af basisklasser, også er defineret i attr, og om disse
> har en værdi, inden jeg kalder ldap_add_s.

Det lyder rimeligt.
Tillader DS er der tilføjes inkonsistent data ?

>
> Håber dette giver en mere præcis beskrivelse af min problemstilling, og
> mine ønsker til løsningen. Det skal understreges, at mine problemer kun
> relatere sig til løsningen af void **objectClass problematikken.

Hvorfor er det smart at smide typeinformation væk, for derefter at have
bøvlet med at genskabe det ?

>
> Til Mogens: Jeg har studeret dit forslag nærmere, og antager du basere
> det på GOF-mønstret Visitor(Design Patterns:331-344). Læser vi nu
> anvendelsesområdet for Visitor (Applicability), synes jeg ikke lige, det
> passer på min problemstilling. 1) En objektstruktur med mange klasser af
> objekter ... - passer ikke her 2) Mange specifikke og urelaterede
> operationer ... - passer ikke her 3) Klassen der definere objektstrukturen
> antages at være en tilnærmet konstant, men man ønsker at definere nye
> operationer på strukturen. Først led passer her, men sidste led gør
> ikke, da operationerne på strukturen også er konstante.
>
> Din løsning ville dog sagtens kunne implementeres, men får jeg gavn af
> mønstret i den her sammenhæng?

Nej, det lyder ikke umiddelbart som om du vil få gavn af det.
Informationen om hvad der er mest konstant og mest varierende fremgik ikke
af den oprindelige spørgsmål, men vil klart påvirke designet af løsningen.

Den almindelige implementering af Visitor giver uheldig kobling med
cirkulære afhængigheder, idet basisklassen via visitorklassen kommer til at
kende alle afledte klasser.
Det er en høj pris, som kræver en betydelig gevindst.

Venlig hilsen

Mogens Hansen



Michael Rasmussen (26-10-2005)
Kommentar
Fra : Michael Rasmussen


Dato : 26-10-05 01:18

On Tue, 25 Oct 2005 19:02:54 +0200, Mogens Hansen wrote:

>
> Jeg er ikke helt sikker på at jeg forstår dig ret, men vær forsigtig
> med at putte atributter i (abstrakte) basisklasser. Definer det interface
> som brugerne (applikations programmøren og designeren af afledte klasser)
> skal bruge.
> Tænk i commonality og variability når man bestemmer hvad der skal være
> i de forskellige klasser.
>
>
Jeg må vist lige have min tænkehat på en ekstra gang

>
> Jeg savner stadig fuldstændigt at se hvorfor det skal være void*, og
> pointer til pointer i stedet for noget mere typesikkert som f.eks.
Problemet er/var, at det ikke er lovligt med en liste af forskellige
structs.

> bool addEntry(..., const vector<schema*>& ...)
> eller måske endda implementere det via et handle-body idiom. Der er et
> skønt eksempel på håndtering af træstruktur på den måde i
> Accelerated C++
> Andrew Koenig, Barbara Moo
> ISBN 0-201-70353-X
> Sourcekoden til programmerne kan iøvrigt downloades fra
> www.acceleratedcpp.com
>
Hvilken side findes dette eksempel? (Jeg har bogen)

>
> Almindeligvis kan det betale sig at overføre en kompliceret struktur
> som map<string, string> by reference i stedet for by value - på den
> måde slipper man for at lave en kopi:
> bool addEntry(...., const map<string, string>&...)
>
Så jeg straks efter, jeg sendte svaret

> Tillader DS er der tilføjes inkonsistent data ?
>
Nej, men DS er optimeret for søgning og læsning. Skrivninger kan være
en bekostelig affære, så derfor bør man sikre sig mod skrivninger, man
på forhånd ved, vil resultere i en fejl.

>
> Hvorfor er det smart at smide typeinformation væk, for derefter at have
> bøvlet med at genskabe det ?
>
Det er det heller ikke, men indtil videre er det den eneste måde, jeg kan
løse det
>
>
> Den almindelige implementering af Visitor giver uheldig kobling med
> cirkulære afhængigheder, idet basisklassen via visitorklassen kommer
> til at kende alle afledte klasser.
> Det er en høj pris, som kræver en betydelig gevindst.
>
Er det ikke problemet i en nødeskal her: Skal det pakkes pænt ind,
kræver det en høj pris, og gevindsten er minimal. Kun et snavset
løsning giver den fornødne gevindst?

--
Hilsen/Regards
Michael Rasmussen
http://keyserver.veridis.com:11371/pks/lookup?op=get&search=0xE3E80917


Mogens Hansen (26-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 26-10-05 06:22


"Michael Rasmussen" <mir@miras.org> wrote in message
news:pan.2005.10.26.00.17.47.28212@miras.org...
> On Tue, 25 Oct 2005 19:02:54 +0200, Mogens Hansen wrote:

[8<8<8<]
> Problemet er/var, at det ikke er lovligt med en liste af forskellige
> structs.

Jo, hvis de har en fælles basisklasse

[8<8<8<]
> Hvilken side findes dette eksempel? (Jeg har bogen)

Kapitel 15, side 269

[8<8<8<]
> Er det ikke problemet i en nødeskal her: Skal det pakkes pænt ind,
> kræver det en høj pris, og gevindsten er minimal. Kun et snavset
> løsning giver den fornødne gevindst?

Jeg tror aldrig at en typeløs løsning kan svare sig, når vi ikke snakker om
meget low level kode.
En pæn, typesikker løsning behøver ikke at være kompliceret.
Templates kan nemt vise sig at være din ven - men det kan jeg komme tilbage
til senere.

Venlig hilsen

Mogens Hansen



Mogens Hansen (26-10-2005)
Kommentar
Fra : Mogens Hansen


Dato : 26-10-05 15:56


"Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
news:435f1296$0$67259$157c6196@dreader2.cybercity.dk...

[8<8<8<]
> Jeg tror aldrig at en typeløs løsning kan svare sig, når vi ikke snakker
> om meget low level kode.
> En pæn, typesikker løsning behøver ikke at være kompliceret.
> Templates kan nemt vise sig at være din ven - men det kan jeg komme
> tilbage til senere.

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <cassert>

using namespace std;

class null_type;

class base_schema
{
public:
virtual ~base_schema()
{}

virtual base_schema* clone() const = 0;

protected:
base_schema()
{}
};

template <typename T>
class schema_container : public base_schema
{
public:
schema_container(const T& t_arg) :
t_(&t_arg) {}

virtual schema_container<T>* clone() const
{ // We could use ref-counting instead
return new schema_container<T>(*this);
}

const T* get() const
{ return t_; }

private:
const T* t_;
};

class schema
{
public:
schema() :
bs_(0) {}
schema(const schema& schema_arg) :
bs_(schema_arg.bs_ ? schema_arg.bs_->clone() : 0) {}
template <typename T>
schema(const T& t_arg) :
bs_(new schema_container<T>(t_arg)) {}
~schema()
{ delete bs_; }
schema& operator=(const schema& schema_arg)
{
// Make copy of new base_schema first for exception safety
std::auto_ptr<base_schema> new_bs(schema_arg.bs_ ?
schema_arg.bs_->clone() : 0);
delete bs_;
bs_ = new_bs.release();
return *this;
}

template <typename T>
const T* get() const
{
if(schema_container<T>* sc =
dynamic_cast<schema_container<T>*>(bs_)) {
return sc->get();
}

return 0;
}

private:
base_schema* bs_;
};

struct person
{
person() {}
person(const string& name_arg, const string& phone_number_arg) :
name(name_arg),
phone_number(phone_number_arg) {}

string name;
string phone_number;
};

struct organizational_unit
{
organizational_unit() {}
organizational_unit(const string& department_arg) :
department(department_arg) {}

string department;
};

void process(const vector<schema>& schemas)
{
for(vector<schema>::const_iterator i = schemas.begin(); schemas.end() !=
i; ++i) {
if(const person* p = i->get<person>()) {
cout << "person:\n"
" " << p->name << "\n"
" " << p->phone_number << endl;
}
else if(const organizational_unit* ou = i->get<organizational_unit>())
{
cout << "organizational unit:\n"
" " << ou->department << endl;
}
else {
cout << "???" << endl;
}
}
}

int main()
{
person per("per", "123456");
organizational_unit personel_resources("personel resources");
organizational_unit r_and_d("R&D");
person ib("ib", "345764");

vector<schema> schemas;

schemas.push_back(per);
schemas.push_back(personel_resources);
schemas.push_back(r_and_d);
schemas.push_back(ib);

process(schemas);
}


Bemærk at det måske kan betale sig at benytte Boost::Variant
(http://www.boost.org/doc/html/variant.html) i stedet.


Venlig hilsen

Mogens Hansen



Michael Rasmussen (26-10-2005)
Kommentar
Fra : Michael Rasmussen


Dato : 26-10-05 21:04

On Wed, 26 Oct 2005 07:22:27 +0200, Mogens Hansen wrote:

>
> Jo, hvis de har en fælles basisklasse
>
Hehe, hønen og ægget problematikken
>
> Kapitel 15, side 269
>
Dejligt, så har jeg lidt godnat-læsning

>
> Jeg tror aldrig at en typeløs løsning kan svare sig, når vi ikke
> snakker om meget low level kode.
> En pæn, typesikker løsning behøver ikke at være kompliceret. Templates
> kan nemt vise sig at være din ven - men det kan jeg komme tilbage til
> senere.
>
Normalt heller ikke noget jeg søger. Jeg fik dog brygget noget sammen i
nat - ikke kønt, men det virker. Har dog set dit forslag med ren
klasseimplementation, som jeg vil prøve at implementere i nat eller i
morgen. Umiddelbart er ideen den samme, men din løsning ser mere robust
ud, og ikke mindst ser den ud til at skalere bedre.

enum SCHEMATYPE {
   dcobject,
   inetorgperson,
   organizationalunit,
   posixaccount,
   posixgroup,
   shadowaccount
};

class schema {
   
   public:
      
      schema(SCHEMATYPE);
      ~schema();
         
      SCHEMATYPE getSchemaType() { return schemaType; };
      void *getSchema() { return currentSchema; };
         
   private:
      SCHEMATYPE schemaType;
      void *currentSchema;

};

schema::schema(SCHEMATYPE type)
{
   schemaType = type;
   switch (schemaType) {
      case dcobject:
         currentSchema = malloc (sizeof(struct dcObject));
         break;            
      case inetorgperson:
         currentSchema = malloc (sizeof(struct inetOrgPerson));
         break;            
      case organizationalunit:
         currentSchema = malloc (sizeof(struct organizationalUnit));
         break;            
      case posixaccount:
         currentSchema = malloc (sizeof(struct posixAccount));
         break;            
      case posixgroup:
         currentSchema = malloc (sizeof(struct posixGroup));
         break;            
      case shadowaccount:
         currentSchema = malloc (sizeof(struct shadowAccount));
         break;            
   }
}

schema:schema()
{
   if (currentSchema)
       free(currentSchema);
}

--
Hilsen/Regards
Michael Rasmussen
http://keyserver.veridis.com:11371/pks/lookup?op=get&search=0xE3E80917


Per Abrahamsen (26-10-2005)
Kommentar
Fra : Per Abrahamsen


Dato : 26-10-05 11:36

Bertel Brander <bertel@post4.tele.dk> writes:

> Hvis alle de class'er der arver fra base class'en og som skal
> indeholdes i listen implementerer de samme funktioner, er det
> naturligvis fint at gøre det med virtuelle functioner i base
> class'en som alle de afledte class'er implementerer.

Du kan lade compileren implementere RTTI alene med dynamic cast. Du
behøver ikke bruge virtuelle funktioner, andet end destruktoren for at
fortælle compileren at den skal generere RTTI.

Hvis man nøjes med at bruge dynamic_cast inde i betingelsen til if
sætninger, har det den fordel at koden er typesikker, i modsætning til
hvis man bruger en enum + reinterp_cast / void* / union.

RTTI er andet og mere end blot virtuelle funktioner.

Søg
Reklame
Statistik
Spørgsmål : 177428
Tips : 31962
Nyheder : 719565
Indlæg : 6407944
Brugere : 218877

Månedens bedste
Årets bedste
Sidste års bedste