Talk:Trait

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(Comment provided by jtulach - via ArticleComments extension)
Line 111: Line 111:
--[[User:JaroslavTulach|JaroslavTulach]] 07:42, 17 September 2012 (UTC)
--[[User:JaroslavTulach|JaroslavTulach]] 07:42, 17 September 2012 (UTC)
 +
== jtulach said ... ==
 +
 +
<div class='commentBlock'>
 +
template<class ITEM>
 +
class llist {
 +
protected:
 +
ITEM *first;
 +
public:
 +
void add(ITEM& p)
 +
{
 +
p.prev=NULL;
 +
p.next=first;
 +
first=&p;
 +
}
 +
void remove(ITEM& p)
 +
{
 +
ITEM *pr=p.prev;
 +
ITEM *nx=p.next;
 +
if(pr) pr->next=nx;
 +
else first=nx;
 +
if(nx) nx->prev=pr;
 +
}
 +
 +
ITEM *get(int idx)
 +
{
 +
ITEM *i=first;
 +
while((i)&&(idx-->0))
 +
i=i->next;
 +
 +
return i;
 +
}
 +
 +
llist(void) { first=NULL; };
 +
~llist() { };
 +
};
 +
 +
// not inherited, requires to define next and prev properties
 +
class person_item {
 +
protected:
 +
int age;
 +
const char* name;
 +
 +
public:
 +
person_item* next;
 +
person_item* prev;
 +
 +
person_item (int age, const char* name) { this->age=age; this->name=name; }
 +
};
 +
 +
 +
template<class ITEM>
 +
class ll_item {
 +
friend llist <ITEM>;
 +
private:
 +
ITEM* next;
 +
ITEM* prev;
 +
};
 +
 +
// inherited from ll_item template which defines next and prev for me
 +
class animal_item : public ll_item <animal_item>{
 +
protected:
 +
const char* name;
 +
 +
public:
 +
 +
animal_item(const char* name) : ll_item <animal_item>() { this->name=name; }
 +
};
 +
 +
int main(int argc, char* argv[])
 +
{
 +
person_item a(10, "Ben");
 +
person_item b(20, "Nora");
 +
person_item c(30, "John");
 +
 +
animal_item x("Fifi");
 +
animal_item y("Bobika");
 +
animal_item z("Bill");
 +
 +
llist <person_item> persons;
 +
persons.add(b);
 +
persons.add(c);
 +
persons.add(a);
 +
// persons.add(x); - compilation fails
 +
 +
llist <animal_item> animals;
 +
animals.add(x);
 +
animals.add(y);
 +
animals.add(z);
 +
}
 +
 +
 +
 +
--jtulach 14:04, 17 September 2012 (CEST)
 +
</div>

Revision as of 12:04, 17 September 2012

Comments on Trait <comments />


Contents

Miles Elam said ...

Yes, this is indeed possible in C++ and is, in fact, used extensively in the C++ standard library (aka STL). For a prime example, look no further than std::string or the various pluggable memory allocators. The example given in the article appears to these eyes as one of a C programmer trying to make C++ do things like C and failing. To be more precise, if one is accessing people objects by iterator, why would a raw pointer to a person need to be manipulated in this way? In addition, what happens if the object must be accessed in multiple ways, e.g., exists in both a normal list and a sorted list (or multiple sorted lists). The C method falls down as there is no single pair of *next and *prev but rather multiple.

Don't get me wrong, C definitely has its uses. Its relative simplicity for one. However, C++'s generic algorithms and data structures should not be discarded so lightly.

--Miles Elam 20:26, 4 September 2012 (CEST)

I am primarily interested in properly typing the multiple class encapsulation case. E.g. having prev/next field in the item class and manipulating them in only by the list. Moreover I'd like to write this (and type this) in a generic way. Looks like it will be possible to do it in C++, but the solution will definitely not be like in Java/Scala - rather upside-down...

--JaroslavTulach 22:25, 5 September 2012 (UTC)

jtulach said ...

The article is more about STL overhead. I personally don't like and don't use STL. I think following C++ implementation is as fast as implementation in C.

class ll_item;
 
class llist {
  public:
    void add(ll_item& p);
    void remove(ll_item& p);
 
    llist(void);
   ~llist();
};
 
class ll_item {
    friend llist;
  private:
    ll_item* next;
    ll_item* prev;
};
 
 
class person_item : public ll_item {
  protected:
    int age;
    const char* name;
 
  public:
    person_item(int age, const char* name);
};
 
class animal_item : public ll_item {
  protected:
    const char* name;
 
  public:
    animal_item(const char* name);
};
 
int main(int argc, char* argv[])
{
  person_item a(10, "Ben");
  person_item b(20, "Nora");
  person_item c(30, "John");
 
  animal_item x("Fifi");
  animal_item y("Bobika");
  animal_item z("Bill");
 
  llist l;
  l.add(b);
  l.add(c);
  l.add(a);
  l.add(x);
  l.add(y);
  l.add(z);
}

--jtulach 21:07, 11 September 2012 (CEST)

Right, now the question is how to generify this (as the example above does not feel type safe enough) - e.g. how to turn into a C++ template?

I don't want to have list of items, but rather list of persons and another list of animals. When I ask for an item from each list, I would expect I get a person in the former case and an animal in the latter. Right now I just get a ll_item. That is efficient, but not really typesafe.

I have an unfinished prototype with template and compared to Java or Scala it feels a bit upside down. Interesting clash of cultures.

--JaroslavTulach 08:26, 12 September 2012 (UTC)

jtulach said ...

I don't know why do you think this is not type safe? You cannot add anything which is not inherited from ll_item. If you have enabled RTTI, you can dynamic cast items from list.

class persons_list : public llist {
  public:
    person_item *get(int idx)
    {
      return dynamic_cast<person_item *>(llist::get(idx));
    }
};

--jtulach 10:58, 13 September 2012 (CEST)

The question Why this is not type-safe? may reveal the progress OOP made during last twenty years. In the middle of nineties Java was claimed a safe language as it could not cause segmentation faults. The C++'s dynamic_cast is a safety of similar kind. It is a runtime check. It is not bad thing, but as I am coding in Java most of the time, I was not calling for something as basic.

When I mentioned type safety, I meant compile type safety. The above dynamic cast does not guarantee that I cannot add animal_item into the llist and try to obtain person_item. To achieve such kind of safety one needs a form of algebraic types (nicely illustrated in the revolutionary essay). Both Scala and Java (since version 1.5) provide a way to marry algebraic types with OOP inheritance and thus provide compile time safety (as code snippets for the direct linked list show).

--JaroslavTulach 07:42, 17 September 2012 (UTC)

jtulach said ...

template<class ITEM> class llist {

 protected:
       ITEM *first;
 public:

void add(ITEM& p)

       {
         p.prev=NULL;
         p.next=first;
         first=&p;
       }

void remove(ITEM& p)

       {
         ITEM *pr=p.prev;
         ITEM *nx=p.next;
         if(pr) pr->next=nx;
         else first=nx;
         if(nx) nx->prev=pr;
       }
       ITEM *get(int idx)
       {
         ITEM *i=first;
         while((i)&&(idx-->0))
           i=i->next;
         return i;
       }
       llist(void) { first=NULL; };
       ~llist() { };

};

// not inherited, requires to define next and prev properties class person_item {

 protected:
 int age;
 const char* name;
 public:
 person_item* next;
 person_item* prev;
 person_item (int age, const char* name) { this->age=age; this->name=name; }

};


template<class ITEM> class ll_item {

 friend llist <ITEM>;

private:

 ITEM* next;
 ITEM* prev;

};

// inherited from ll_item template which defines next and prev for me class animal_item : public ll_item <animal_item>{

 protected:
 const char* name;
 public:
 animal_item(const char* name) : ll_item <animal_item>() { this->name=name; }

};

int main(int argc, char* argv[]) {

 person_item a(10, "Ben");
 person_item b(20, "Nora");
 person_item c(30, "John");
 animal_item x("Fifi");
 animal_item y("Bobika");
 animal_item z("Bill");
 llist <person_item> persons;
 persons.add(b);
 persons.add(c);
 persons.add(a);

// persons.add(x); - compilation fails

 llist <animal_item> animals;
 animals.add(x);
 animals.add(y);
 animals.add(z);

}


--jtulach 14:04, 17 September 2012 (CEST)

Personal tools
buy