# Компьютеры, телекоммуникации, ПО... > Программирование >  C++ haters handbook :-)

## homo ludens

прошу модераторов перенести сюда ветку из топика C++ vs Java начиная с поста 88

----------


## THRESHE

Мдя никогда не думал что будут сравнивать С и С++  

На С все равно ничего большого не напишешь.
*2 homo ludens* и чем же С лучше чем С++ ?

----------


## homo ludens

> крайне любопытно посмотреть на пример не нооп абстракций, в котором совсем ничего не пробрасывается между уровнями. все упирается в то, каким данным это разрешить, а каким - нет, т.е. в дизайн интерфейсов


 практический любой правильный С-интерфейс. Правильные С-интерфейсы можно легко найти в glibc и сопутствующих продуктах. Возьмите например POSIX threads (pthread_t).

давайте напишем интерфейс на С объекта transglukator.

что мы имеем на С
два h-файла.
transglukator.h
transglukator_int.h
transglukator.c

******* в transglukator.h
typedef что-то transglukator_t. 
//что-то обычно либо void* либо int
//в обоих случаях это хендл с неизвестной снаружи внутренней структурой.

transglukator_t transglukator_init(...);
int transglukator_destroy(transglukator_t);

******* в transglukator_int.h

typedef struct
{
 ... internal representation of 
} transglukator_int_t;

// плюс внутренние структуры, никому не интересные снаружи, но которые могут зависеть от других библиотек и h-файлов

******* в transglukator_int.c
#include "transglukator.h"
#include "bukazoid.h"
#include "transglukator_int.h"

// реализация

что бы мы имели на С++ в худшем варианте

******* в transglukator.h
class transglukator
{
  public:
     transglukator(...);
     ~transglukator(void)
  private:
....
};

******* в transglukator.с

transglukator::transglukator(...)
{
 ...
}

transglukator::~transglukator(void)
{
 ...
}

...

чем хуже реализация С++?

а вот чем.
мы можем зафиксировать на начальном этапе проекта интерфейсы. Но мы не можем зафиксировать реализации. Появление в private скрытой внутренней переменной вызывает полную перекомпиляцию проекта использующего transglukator.
В С-реализации требуется только перелинковка, более того, если вы пишете сервер, работающий 365x24x7 то даже этого не надо - выносите код трансглюкатора в dll и перегружаете ее не останавливая сервера.
Второй минус. Если для внутренней реализации трансглюкатора требуется определение класса bukazoid, то это автоматически означает, что все определения класса буказоид и все его интерфейсы включаются в путь h-файлов (это к тому, почему я никогда не пользую std::abs - посмотрите сколько мусора он за собой тянет).  Время компиляции возрастает офигенно. Связность исходников - до упора.
Можно выкрутиться двумя трюками.
1. написать псевдоадаптер - пустой класс-оболочку только с вызовами другого класса. без динамических переменных это обычно сложно. Кроме того лапша этих псевдоадаптеров намного хуже вермишели макросов, макрос - это одна-две строчки, а псевдоадаптер - это файл. Читаемость кода при этом тоже не повышается. Зато повышается его связность.
Кстати для темплейтов эта практика просто необходима, нормально реализованный шаблон - это только интерфейсы к единой для всех его реализаций библиотеке.
Как вы думаете, почему stl написан на С? 
2. напрямую использовать С-шные методы описанные выше.
метод с хендлом типа int (юзается когда надо поддерживать целостность коллекции трансглюкаторов) работает. А вот с void* иногда бывают проблемы.
Почему? А потому что умники из комитетов по религиозно-объектной чистоте постановили - void* должен явно преобразовываться к типу. Одним этим выстрелом они убили много зайцев - один  из которых - уничтожение обратной совместимости с С. Неслабо, да?




> правильно ли я понимаю, что функциональность шире, чем у ассемблера, невозможна ?


 Возможна конечно, хотя с частичной потерей эффективности. Почему нет? Просто абстракции должны иметь поменьше дырок. Более того, потеря функциональности тоже возможна, но функциональность не должна подменяться удобством. Это абсолютно разные вещи. try/catch не расширяет функциональность, также как и uBlas. Иначе мы получаем то что имеем - из случайно выбранных 10 проектов на sf.net 9 будут просто оболочками-адаптерами к чужому коду, оболочками с нулевой функциональностью, но удобным (не для всех) интерфейсом. Удобство - категория субъективная, а функциональность - нет. Вот и весь критерий.

продолжаем? я еще не на все вопросы ответил...

----------


## TechInsightJobs

если у тебя интерфейс предназначен для взаимодействия с другими компонентеми - его проектируют как чисто интерфейсный и скрывают реализацию

говорить о том что на макросах проще построить скрытие реализации чем на с++ или на той же java - это притягивать за уши






> практический любой правильный а вот чем.
> мы можем зафиксировать на начальном этапе проекта интерфейсы. Но мы не можем зафиксировать реализации. Появление в private скрытой внутренней переменной вызывает полную перекомпиляцию проекта использующего transglukator.
> В С-реализации требуется только перелинковка, более того, если вы пишете сервер, работающий 365x24x7 то даже этого не надо - выносите код трансглюкатора в dll и перегружаете ее не останавливая сервера.
> Второй минус. Если для внутренней реализации трансглюкатора требуется определение класса bukazoid, то это автоматически означает, что все определения класса буказоид и все его интерфейсы включаются в путь h-файлов (это к тому, почему я никогда не пользую std::abs - посмотрите сколько мусора он за собой тянет).  Время компиляции возрастает офигенно. Связность исходников - до упора.
> продолжаем? я еще не на все вопросы ответил...

----------


## homo ludens

> если у тебя интерфейс предназначен для взаимодействия с другими компонентеми - его проектируют как чисто интерфейсный и скрывают реализацию
> 
> говорить о том что на макросах проще построить скрытие реализации чем на с++ или на той же java - это притягивать за уши


 я нигде в своем посте не писал о макросах.
я писал о разделении уровней и понижении связности кода. Макросы в этом примере не используются нигде.
То, что проектировать интерфейс надо правильно - банальность. Но на С++  почему-то редко так делают.  Ваш вариант проектирования - это трюк 1-го типа из перечисленных в списке. Который полностью совпадает с С-реализацией если после typdef поставить скобки class transglukator { public: ..... };

А вот практический пример такой реализации.

******************* C++ x.cc
#include <algorithm>

int main(void)
{
  int x=-1;
  return std::abs(x);
}

******************* C  y.c
#include <math.h>

int main(void)
{
  int x=-1;
  return labs(x);
}

казалось бы - какая разница?

а вот какая
gcc -E x.cc | wc -l
15280
gcc -E y.c | wc -l
663

нафига мне для std::abs  итераторы? Которые включаются в общий обход h-файлов.
Какое будет время компиляции для проекта из 100к строк?
350 килобайт текста! Чтобы вычислить abs? 
А это стандартная библиотека. Заглянем в boost?

Ладно, это компилятор. Фиг с ним, а что будет при
gcc -S x.cc ; wc -l <x.s
42
gcc -S y.c ; wc -l <y.s
27

Справедливости ради замечу, что исполняемый код практически одинаков, вся разница в добавлении ссылок на pthread. Но на хрена они тут нужны, мне совершенно непонятно.

причем это системная библиотека с гарантированно стабильным интерфейсом. А что будет в reallife?

----------


## TechInsightJobs

"Кроме того лапша этих псевдоадаптеров намного хуже вермишели макросов, макрос - это одна-две строчки,"

а механизм абстрактных классов и виртуальные функции это встроенные средства языка

про правильное проектирование на с++ - это делают гораздо чаще чем на с
думаю дизайн документов для с++ проектов гораздо больше чем для С

тут вообще другой момент - говоря про проектирование, Вы все время уходите в компилирование и линкование
это вообще разные вещи. жизнь проекта с неправильным дизайном и отточенной компиляцией\линкованием гораздо меньше чем проекта с правильным дизайном







> я нигде в своем посте не писал о макросах.
> я писал о разделении уровней и понижении связности кода. Макросы в этом примере не используются нигде.
> То, что проектировать интерфейс надо правильно - банальность. Но на С++  почему-то редко так делают.  Ваш вариант проектирования - это трюк 1-го типа из перечисленных в списке. Который полностью совпадает с С-реализацией если после typdef поставить скобки class transglukator { public: ..... };
> 
> А вот практический пример такой реализации.
> 
> ******************* C++ x.cc
> #include <algorithm>
> 
> ...

----------


## pal

> чем хуже реализация С++?
> 
> а вот чем.


 мы ведь не будем ставить в упрек с++ плохость вашей реализации на нем ?



> (это к тому, почему я никогда не пользую std::abs - посмотрите сколько мусора он за собой тянет).


 вы его не используете по другой причине: вы не знаете, что он определен в <cmath>, а не в <algorithm>



> 1. написать псевдоадаптер - пустой класс-оболочку только с вызовами другого класса. без динамических переменных это обычно сложно. Кроме того лапша этих псевдоадаптеров намного хуже вермишели макросов, макрос - это одна-две строчки, а псевдоадаптер - это файл. Читаемость кода при этом тоже не повышается. Зато повышается его связность.


 если речь об идиоме pimpl, то ее используют с несколько более широкими целями. можно реализовать value semantics, что вообще невозможно на с, на худой конец, можно удостовериться что обьект будет правильно разрушен.
у вас же в случае void * его можно удалить free с потерей всех остальных ресурсов, либо в случае int можно делать очень много других ненужных операций, а в конце все равно не удалить 
при желании на с++ можно делать точно так же, как и на с:
class A; A * f ( ); void g ( A * ); ...
в качестве дополнительного бонуса вы не передадите его функции, которая ждет тоже void * или int, но на самом деле совсем другой, также delete на неопределенный класс будет ругаться ( помните, я говорил не использовать malloc/free ? ). только так делать не надо, потому что кто-то обязательно забудет его удалить.
еще можно использовать абстрактный базовый класс и возвращать smartpointer на конкретную реализацию, если вам не жалко виртуальных функций.

как мы видим, на с++ можно сделать точно так же, только лучше, а можно слегка по другому, но еще лучше



> Кстати для темплейтов эта практика просто необходима, нормально реализованный шаблон - это только интерфейсы к единой для всех его реализаций библиотеке.
> Как вы думаете, почему stl написан на С?


 я знаю, что он написан на с++. где вы такое видели ?
вы, похоже, шаблоны с чем-то другим перепутали



> 2. напрямую использовать С-шные методы описанные выше.


 как я уже показал, так делать нельзя и не надо никогда



> метод с хендлом типа int (юзается когда надо поддерживать целостность коллекции трансглюкаторов) работает. А вот с void* иногда бывают проблемы.
> Почему? А потому что умники из комитетов по религиозно-объектной чистоте постановили - void* должен явно преобразовываться к типу. Одним этим выстрелом они убили много зайцев - один  из которых - уничтожение обратной совместимости с С. Неслабо, да?


 учитывая, что к void * преобразовывается автоматом, разрешение обратного преобразования одним махом сводит на нет всю систему типов - любой тип автоматически превращается в любой другой через void *.
может, вам просто ближе к сердцу языки с динамической типизацией и рантайм проверками ? это не стыкуется с вашей обеспокоенностью эффективностью исполнения.

так же нет никакой проблемы в этой несовместимости с с. разве, что вы захотите перевести программу с с на с++. тогда надо радоваться, заодно несколько ошибок вокруг void* обнаружите 



> Возможна конечно, хотя с частичной потерей эффективности. Почему нет? Просто абстракции должны иметь поменьше дырок.


 с этим трудно не согласиться, но как вы из этого сделали вывод, что абстракций быть не должно ?



> Более того, потеря функциональности тоже возможна, но функциональность не должна подменяться удобством. Это абсолютно разные вещи. try/catch не расширяет функциональность, также как и uBlas. Иначе мы получаем то что имеем - из случайно выбранных 10 проектов на sf.net 9 будут просто оболочками-адаптерами к чужому коду, оболочками с нулевой функциональностью, но удобным (не для всех) интерфейсом. Удобство - категория субъективная, а функциональность - нет. Вот и весь критерий.


 я не могу разобраться в вашей терминологии. чем отличается функциональность от удобства, если преимуществом с по сравнению с ассемблером является функциональность, а преимуществом с++ по сравнению с с - удобство ? или у вас есть с, который каким-то сверхъестественным образом выполняется процессором телепатически с большей функциональностью, чем ассемблер ?

----------


## pal

> Кроме того лапша этих псевдоадаптеров намного хуже вермишели макросов, макрос - это одна-две строчки, а псевдоадаптер - это файл


 попробуйте на досуге найти в /usr/include/c++/4.1.*/tr1/ реализацию std::tr1::bind. а там даже goto в макросах нет 
но это все мелочи по сравнению с тем, что бывает когда кто-то переопределяет malloc/free/realloc, и при этом обменивается указателями с модулями, где ничего такого не переопределено.

----------


## pal

> Правильные С-интерфейсы можно легко найти в glibc и сопутствующих продуктах. Возьмите например POSIX threads (pthread_t).


 я боюсь, что вы выбрали не совсем правильный интерфейс 
на весь bits/pthreadtypes.h целых 4 инта: pthread_t, pthread_key_t, pthread_once_t, pthread_spinlock_t
знаете, почему ? потому что они и являются интами 
остальные типы делятся на две группы:
typedef union
{
  struct
  {
    int __lock;
    unsigned int __futex;
    __extension__ unsigned long long int __total_seq;
    __extension__ unsigned long long int __wakeup_seq;
    __extension__ unsigned long long int __woken_seq;
    void *__mutex;
    unsigned int __nwaiters;
    unsigned int __broadcast_seq;
  } __data;
  char __size[__SIZEOF_PTHREAD_COND_T];
  __extension__ long long int __align;
} pthread_cond_t;

typedef union
{
  char __size[__SIZEOF_PTHREAD_CONDATTR_T];
  int __align;
} pthread_condattr_t;

----------


## homo ludens

> мы ведь не будем ставить в упрек с++ плохость вашей реализации на нем ?


   Я ведь сразу сказал, что это худший вариант.
Однако куча программеров так пишет. Более того для многих junior'ов это нормальный код. :- (




> вы его не используете по другой причине: вы не знаете, что он определен в <cmath>, а не в <algorithm>


 потому что с <cmath> у меня это вообще не компилится. Бедный компилятор не может выбрать какую из перегруженных функций ему выполнять - float, double или long double. 




> у вас же в случае void * его можно удалить free с потерей всех остальных ресурсов, либо в случае int можно делать очень много других ненужных операций, а в конце все равно не удалить ; )


 Удалять его free никому в голову не придет. А насчет забыть - так для динамически определяемой памяти это верно и для С++. Не юзать динамическую память без обверток?
Вы с windows handle тоже делаете много ненужных вещей?
Идея в том, что обычно transglukator_t появляется только в вызовах функций собственного интерфейса. Или в промежуточных переменных. Никому в голову не придет допустим складывать объекты этого типа. Кроме программиста С++ привычного к перегрузкам операторов. : -)



> как мы видим, на с++ можно сделать точно так же, только лучше, а можно слегка по другому, но еще лучше


 На С++ это можно сделать сложнее и единственный плюс, который я здесь вижу - контроль  типов, который таки да полезен, когда мы его действительно хотим видеть. И вреден, когда мы его не хотим видеть.  
Кроме того, кто-то из известных программистов сказал очень умную вещь - хороший язык, это тот, в котором правильную вещь можно сделать единственным способом.
А я добавлю от себя - правильная реализация в хорошем языке должна отличаться от неправильных тем, что имеет самый компактный и элегантный интерфейс, тем самым создавая общую эстетику для групп программистов и облегчая взаимное понимание кода.
В С++ эти правила не выполняются.
Есть правда пару нюансов с наследованием, но имхо в правильной программе наследование должно юзаться реже чем longjump.




> я знаю, что он написан на с++. где вы такое видели ?
> вы, похоже, шаблоны с чем-то другим перепутали


 Изначально библиотека писалась на С. Потом была переделана на С++, но внутри это выглядело как C+generic programmig, разобраться там было достаточно сложно, код сверхсвязный. Впрочем внутри я рассматривал раниие реализации от HP и  достаточно давно. Однако в википедии написано вот что.




> Careless use of STL templates can lead to code bloat. This has been countered with special techniques within STL implementation (using void* containers internally) and by improving optimization techniques used by compilers.


 Я тоже не представляю себе эффективной реализации контейнеров по другому как в виде объектных интерфейсов к void* контейнерам. Возможно вы меня просветите.




> так же нет никакой проблемы в этой несовместимости с с. разве, что вы захотите перевести программу с с на с++. тогда надо радоваться, заодно несколько ошибок вокруг void* обнаружите


 С-программа не компилируется в общем случае на С++. Предупреждение о конверсии void* можно было сделать warning'ом. Тогда бы этих проблем не было бы и все были бы довольны.



> с этим трудно не согласиться, но как вы из этого сделали вывод, что абстракций быть не должно ?


 они не должны быть дырявыми либо если уж они дырявы, должна быть возможность эти дырки заткнуть. Абстракция должна давать больше чем забирать. Пример - интерфейсы файловых дескрипторов (open/close/read/write). Этот интерфейс очень абстрактен, потому дыряв. Работая с устройством или с сокетом или с файлом на диске вы работаете в общей абстракции. Но когда ее не хватает - есть ioctl



> я не могу разобраться в вашей терминологии. чем отличается функциональность от удобства, если преимуществом с по сравнению с ассемблером является функциональность, а преимуществом с++ по сравнению с с - удобство ? или у вас есть с, который каким-то сверхъестественным образом выполняется процессором телепатически с большей функциональностью, чем ассемблер ?


 С более функционален, чем ассемблер. В момент появления С все операционки писались на ассемблере конкретной машины. После его появления стало возможным писать их для любой машины. Это - пример расширения функциональности. С++ такого расширения не дает. Нет прикладных задач, которые можно решить на С++ но нельзя решить на С.

Концепция функциональности против удобства на самом деле достаточно сложна для понимания и потребует не менее страницы постов. Могу написать в свободное время.





> но это все мелочи по сравнению с тем, что бывает когда кто-то переопределяет malloc/free/realloc, и при этом обменивается указателями с модулями, где ничего такого не переопределено.


 это действительно маразм, причем фатальный. Однако в реальной жизни переопределения обычно делают либо для локальной библиотеки в период отладки, либо глобально для всего проекта (mtrace), опять таки на период отладки.




> знаете, почему ? потому что они и являются интами


 они не могут являться интами!!!
Это дескрипторы в таблице, другой вопрос, что таблица может храниться в ядре ОС, а может обрабатываться в библиотеке. В любом случае pthread_t - это куча указателей и дескрипторов. Если файловый дескриптор POSIX интерфейса (на котором весь инет держится) - int, это же не значит, что он реально целый.  Это индекс в таблице значительно более сложной, чем то, что мы видим. Нету меня здесь сырцов glibc под рукой, они ж огромные, их тянуть задолбаешься. Одни h-файлы. 

Кажется мне пора приводить примеры практических вещей, которые можно сделать на С и нельзя на С++
: -)

----------


## homo ludens

Кстати эти структуры в pthread.h там тоже не зря определены. Дело в том, именно их иногда нужно инициализировать статически. А вот pthread_t статически инициализировать никому в голову не придет. Поэтому он - int.

----------


## TechInsightJobs

> Есть правда пару нюансов с наследованием, но имхо в правильной программе наследование должно юзаться реже чем longjump.


 гы
в принципе дальше можно уже и не диспутировать

----------


## pal

> Кстати эти структуры в pthread.h там тоже не зря определены. Дело в том, именно их иногда нужно инициализировать статически. А вот pthread_t статически инициализировать никому в голову не придет. Поэтому он - int.


 вы действительно считаете, что pthread_once_t можно инициализировать как-то иначе, чем статически ?
более того, не все из определенных структур имеют макросы для инициализации. т.е. вы не правы в обе стороны. только pthread_t на самом деле является указателем на структуру, потому что ее выделять нельзя что статически, что динамически.

----------


## homo ludens

> гы
> в принципе дальше можно уже и не диспутировать


 Тогда дискутируйте с монстрами ООП - это они рекомендуют вместо наследования использовать композицию везде где это можно. Есть даже такое выражение - anti-pattern yo-yo. Кажется даже в GoF это было, хотя могу ошибаться, текстов под рукой нет.



> вы действительно считаете, что pthread_once_t можно инициализировать как-то иначе, чем статически ?
> более того, не все из определенных структур имеют макросы для инициализации. т.е. вы не правы в обе стороны. только pthread_t на самом деле является указателем на структуру, потому что ее выделять нельзя что статически, что динамически.


 Инициализируются статически как раз те структуры, которые представлены не хендлами. 
Я могу написать pthread_cond_t x=PTHREAD_COND_INITIALIZER
там как раз все макросы описаны такого типа.
на первый взгляд как бы изящней их енумераторами сделать, но если человеку надо будет работать с членами структуры, то этот метод - комбинирует все достоинства и самый простой.
чтобы было чем заткнуть дырки в абстракциях.

----------


## pal

> потому что с <cmath> у меня это вообще не компилится. Бедный компилятор не может выбрать какую из перегруженных функций ему выполнять - float, double или long double.


 замените компилятор



> Удалять его free никому в голову не придет.


 почему не придет ? void* так и напрашивется на free



> А насчет забыть - так для динамически определяемой памяти это верно и для С++.


 это только в максимально приближенном варианте. в случае с pimpl или с умными указателями все само удалится.



> Не юзать динамическую память без обверток?


 совершенно верно ( максимально, на сколько это возможно )



> Вы с windows handle тоже делаете много ненужных вещей?


 не пользуюсь. но любой инт можно случайно передать вместо другого инта, скажем, в функцию, получающую несколько интов. и компилятор это проглотит, не поперхнувшись. в случае же указателя на уникальный класс, будет ошибка.



> Идея в том, что обычно transglukator_t появляется только в вызовах функций собственного интерфейса. Или в промежуточных переменных. Никому в голову не придет допустим складывать объекты этого типа. Кроме программиста С++ привычного к перегрузкам операторов. : -)


 зачем же складывать. представьте, что у вас есть 10 разных подсистем, в каждой из которых будет void *.
что помешает вызвать функцию подсистемы а с указателем от подсистемы б ?



> На С++ это можно сделать сложнее и единственный плюс, который я здесь вижу - контроль  типов, который таки да полезен, когда мы его действительно хотим видеть. И вреден, когда мы его не хотим видеть.


 не хотите - в с++ есть reinterpret_cast. однако, он не произойдет вдруг, сам собой, когда его никто не ждал.
контроль типов не просто полезен, я вообще не представляю, как без него писать программы, сложнее helloworld



> Кроме того, кто-то из известных программистов сказал очень умную вещь - хороший язык, это тот, в котором правильную вещь можно сделать единственным способом.
> А я добавлю от себя - правильная реализация в хорошем языке должна отличаться от неправильных тем, что имеет самый компактный и элегантный интерфейс, тем самым создавая общую эстетику для групп программистов и облегчая взаимное понимание кода.
> В С++ эти правила не выполняются.


 ну вот, с нашими типами, правильные реализации элегантны, а reinterpret_cast - нет. у вас есть какой-то контр-пример ?



> Есть правда пару нюансов с наследованием, но имхо в правильной программе наследование должно юзаться реже чем longjump.


 наследование, конечно, надо применять к месту, а вот longjmp в хороших программах вообще не используют.



> Изначально библиотека писалась на С. Потом была переделана на С++, но внутри это выглядело как C+generic programmig, разобраться там было достаточно сложно, код сверхсвязный.


 может все-таки, это был с++, просто это было давно и с++ тогда был немного другой ? ну и вы должны понимать, что инлайн без связности не получится



> Впрочем внутри я рассматривал раниие реализации от HP и  достаточно давно.


 ну, с тех пор не только реализации, но и спецификации сильно поменялись



> Я тоже не представляю себе эффективной реализации контейнеров по другому как в виде объектных интерфейсов к void* контейнерам. Возможно вы меня просветите.


 это касается только контейнеров указателей и эффективности в терминах объема сгенерированного кода, когда вы используете много однотипных контейнеров разных типов указателей.
это всего лишь частный случай ситуации, когда шаблон имеет много операций, не зависящих от его параметров, тогда их выделяют в нешаблонный предок, чтобы не генерить один и тот же код для шаблонов с разными параметрами.



> С-программа не компилируется в общем случае на С++.


 и не должна. достаточно иметь возможность подключать с заголовки. при особом желании в следующем стандарте с могут озаботиться о совместимости и о программистах, как уже сделали местами в с99



> Предупреждение о конверсии void* можно было сделать warning'ом. Тогда бы этих проблем не было бы и все были бы довольны.


 как я уже говорил, это будет дырой в системе типов. это все равно, что предложить warning на любое недозволенное преобразование. какой в этом смысл, если в том месте, в котором это действительно нужно ( а есть ли такое ? ), можно прописать явное приведение, которое будет бросаться в глаза.



> они не должны быть дырявыми либо если уж они дырявы, должна быть возможность эти дырки заткнуть. Абстракция должна давать больше чем забирать. Пример - интерфейсы файловых дескрипторов (open/close/read/write). Этот интерфейс очень абстрактен, потому дыряв. Работая с устройством или с сокетом или с файлом на диске вы работаете в общей абстракции. Но когда ее не хватает - есть ioctl


 интерфейс дескрипторов плох, ioctl еще хуже, но я в них вижу демонстрацию недостатков с по сравнению с с++



> С более функционален, чем ассемблер. В момент появления С все операционки писались на ассемблере конкретной машины. После его появления стало возможным писать их для любой машины. Это - пример расширения функциональности. С++ такого расширения не дает.


 да и с не дает, т.к. он не первый язык высокого уровня



> Нет прикладных задач, которые можно решить на С++ но нельзя решить на С.


 никто и не спорит. однако на с++ их решать удобнее



> это действительно маразм, причем фатальный. Однако в реальной жизни переопределения обычно делают либо для локальной библиотеки в период отладки, либо глобально для всего проекта (mtrace), опять таки на период отладки.


 проблема в том, что ни компилятор, ни линкер не смогут проследить за тем, что эта библиотека не обменивается указателями с окружающим миром. как раз с "библиотекой для отладки" я и сталкивался 



> они не могут являться интами!!!
> Это дескрипторы в таблице, другой вопрос, что таблица может храниться в ядре ОС, а может обрабатываться в библиотеке. В любом случае pthread_t - это куча указателей и дескрипторов.


 pthread_t это действительно указатель на структуру, маскирующийся интом
pthread_key_t это инт, являющийся индексом массива
pthread_once_t это просто инт в виде флагов
pthread_spinlock_t это тоже флаг



> Если файловый дескриптор POSIX интерфейса (на котором весь инет держится) - int, это же не значит, что он реально целый.  Это индекс в таблице значительно более сложной, чем то, что мы видим. Нету меня здесь сырцов glibc под рукой, они ж огромные, их тянуть задолбаешься. Одни h-файлы.


 дескиптор это индекс, но не от хорошей жизни, а в целях безопасности. причем, он совсем не защищает программу от ее ошибки, только другие программы от злонамеренной программы.



> Кажется мне пора приводить примеры практических вещей, которые можно сделать на С и нельзя на С++
> : -)


 поддерживаю

----------


## pal

> Инициализируются статически как раз те структуры, которые представлены не хендлами. 
> Я могу написать pthread_cond_t x=PTHREAD_COND_INITIALIZER
> там как раз все макросы описаны такого типа.


 структур всего 9, инициализаторы имеют 3, плюс pthread_once_t, который не структура. я все-таки настаиваю на том, что его иначе, чем статически, инициализировать невозможно.

----------


## pal

у меня есть теория
pthread_t - единственный скрытый указатель, потому что эту структуру клиентский код самостоятельно ( без хелперов ) размещать не имеет никакой возможности. все остальные можно размещать в стеке/статически, т.е. моментально, следовательно, при дизайне озаботились в первую очередь  о скорости

----------


## TechInsightJobs

не думаю что мне придется с ними диспутировать по поводу частоты употребления наследования по сравнению с лонгджампами

gof не содержит антипаттернов

ну и если вы его упомянули - то он говорит только о том что надо наследование использовать в меру и не строить длиныых наследований

что в той же мере относится к лонгджампами - если их рассовать не в меру то понимание у разработчика исчезнет гораздо раньше чем при длинном наследовании




> Тогда дискутируйте с монстрами ООП - это они рекомендуют вместо наследования использовать композицию везде где это можно. Есть даже такое выражение - anti-pattern yo-yo. Кажется даже в GoF это было, хотя могу ошибаться, текстов под рукой нет.

----------


## homo ludens

> gof не содержит антипаттернов


 sorry, я сразу сказал, что исходников нет под рукой.
атипаттерны тем не менее существуют. как и антипаттерн yo-yo.
ИМХО GoF кажется была фраза inheritance breaks encapsulation?
Так вот для меня encapsulation ценнее inheritance. Поэтому ценность inheritance для меня лично падает ниже логджампа.
Кстати о лонгджампе.
А вы никогда не использовали его с функциями типа alloca и strdupa? Он неожиданно начинает выглядеть довольно милым.

----------


## homo ludens

> замените компилятор


 на двух тачках с разными архитектурами.
gcc version 3.4.4 20050526
gcc version 4.1.0

x.cc:6: error: call of overloaded 'abs(int&)' is ambiguous
/usr/include/c++/4.1.0/cmath:89: note: candidates are: double std::abs(double)
/usr/include/c++/4.1.0/cmath:93: note:                 float std::abs(float)
/usr/include/c++/4.1.0/cmath:97: note:                 long double std::abs(long double)





> почему не придет ? void* так и напрашивется на free


 какой void*? человек видит transglukator_t. 




> зачем же складывать. представьте, что у вас есть 10 разных подсистем, в каждой из которых будет void *.
> что помешает вызвать функцию подсистемы а с указателем от подсистемы б ?


 нету void*. человек делает вызов trasglukator_t t=transglukator_init(); Зачем его передавать потом в bukazoid_destroy ?
что мешает программисту взять кубический корень из windows handle? Очевидно здравый смысл.




> как я уже говорил, это будет дырой в системе типов. это все равно, что предложить warning на любое недозволенное преобразование. какой в этом смысл, если в том месте, в котором это действительно нужно ( а есть ли такое ? ), можно прописать явное приведение, которое будет бросаться в глаза.


 стандартный вариант компиляции - ошибка. компиляция с взведенным флагом - ворнинг. компиляция с флагом совместимости - без сообщений. Все, больше ничего не надо и время нас рассудило бы. Может через 20 лет лишние флаги бы умерли за ненадобностью. 
У меня CXXFLAGS установлены в одно, у кого-то - в другое значение. Все довольны.
Расскажите, почему этого нельзя было сделать. Почему я могу компилировать K&R программы многолетней давности на С и немогу это делать на С++. Нарушение обратной совместимости - имхо признак дикой неряшливости дизайна.
И надо очень сильно молиться на систему типов, чтобы поставить ее выше в своей системе ценностей.




> да и с не дает, т.к. он не первый язык высокого уровня


 Ознакомьтесь с историей и целями создания С и UNIX. С - первый язык высокого уровня, который позволил эффективно писать низкоуровневые блоки. До С все ядра ОС писались на ассемблере и портируемость ОС ограничивалась двумя-тремя архитектурами. Вместе С была создана первая коммерческая ОС полностью написанная на машинно-независимом языке. Эта ОС (UNIX) живет до сих пор.
Ничего близкого С++ не создавал и не создаст. Нет задач, которые может решить только С++. Были задачи которые смог решить только С. Они есть и сейчас, никто не мешает написать свой драйвер ядра на С++. Но почему-то желающих нет. А если находятся, то не находятся желающие этим драйвером пользоваться.




> никто и не спорит. однако на с++ их решать удобнее


 Удобство - критерий субъективный. Мне неудобнее. Программисту, которому на выбор надо выучить с нуля С и написать на нем проект, либо программисту который должен с нуля выучить С++ + ООП + все аббревиатуры + все шаблоны + как правильно писать (это ведь нетривиальная задача  неудобнее. Програмисту, который матюкаясь читает код, переполненный перегруженными функциями и операторами и 10 (! сам видел) уровнями наследования - неудобнее, но это он матюкается пока не сел за отладчик, вот тут он поймет, что такое неудобно. Руководству eBay в проектах которого количество членов класса превышало возможности компилятора - неудобно.
Давайте лучше к объективным критериям вернемся.





> проблема в том, что ни компилятор, ни линкер не смогут проследить за тем, что эта библиотека не обменивается указателями с окружающим миром. как раз с "библиотекой для отладки" я и сталкивался


 Есть ли жизнь за пределами компилятора? Или лучше так - если компилятор чего-то не может, то этого не может никто
Если для вас даже задачи статического чекинга должны выполняться компилятором, то я даже не знаю что сказать.
Есь очень много программ, работающих с кодом напрямую без компилятора. На С++ их меньше, так как у С++ усложненный синтаксис. Что убъет язык  в тот момент, когда на рынок придут функциональные языки индустриального класса. 
Что характерно, С выживет.
Кстати, вы с openC++ не работали?




> pthread_once_t это просто инт в виде флагов
> pthread_spinlock_t это тоже флаг


 если я контролирую thread pool, то мне может понадобиться массив pthread_once_t. И в этом случае он действительно удобней как int и инициализация будет в цикле.
А вот массив cond_t либо condattr_t никому не нужен, обычно это один объект, на который ссылаются другие.
Хотя конечно, при работе с pthread эффективность всегда на первом месте, удобство пользования все-таки есть, но это не удобство в понимании объектников (пусть придет мама (необъектная) и сделает мне хороший компилятор, который все сделает за меня), это аскетическая простота использования и минимализм.
pthread не относится напрямую к glibc, я его просто с потолка выбрал, по причине простоты. А вообще glibc - относительно старая библиотека с феноменальной портируемостью и очень неплохой эффективностью. Т.е. в моих терминах показатели функциональности и эффективности на высоте, удобства - в приемлимых рамках. 
Я не думаю, что boost при своей идеологии в состоянии повторить эту аскетичность и простоту.

----------


## homo ludens

обещанный пример из реальной жизни где работает С и не работает С++.
будет состоять из нескольких постов.

часть первая.
итак возьмем два блока кода. Однис с malloc, другой с обычным порошком - try/catch
первый блок я напишу сам, причем с ошибками, и не используя разных полезных техник (например alloca).
Второй код - аналог на try/catch я попрошу написать кого-то из присутствующих здесь объектников.
Итак. Инициализатор структуры foo.


```
typedef void* foo_t;
typedef struct
{
   bar_t bar;
   baz_t baz;
} foo_int_t;

foo_t foo_init(int z)
{
   foo_int_t* rv=0;
   
   if(!(rv=malloc(sizeof(foo_int_t)))
     return 0;

  {
   char *p=malloc(14);  // имитируем необходимость в динамической памяти,
                                      // забыли проверить на 0
   
   if(!(rv->bar=bar_init()))
      goto leave;	// забыли освободить память

    bar_setmem(p);      // реально если p требуется в инициализации, то должен быть там. но мы ведь пишем неправильно. :-)
    free(p);
  } 
   if(rv->baz=baz_init())
     return rv;

    free(rv->baz);
  leave:
    free(rv->bar);
    free(rv);    
}
```

 А теперь ждем  его функциональный аналог аналог на С++, причем написанный правильно (оказывается очень многие вещи на С++ можно писать неправильно!)

----------


## Ull9

смотрел смотрел. не понял. Конечно автор предупредил
что будет сошибками...
ну допустим чего нибудь такое...
но если знать что поинтер foo_t будет использоваться только
в с++ коде. написал бы еще проще.

typedef void* foo_t;
void release_foo_t (foo_t);
typedef struct
{
   bar_t bar;
   baz_t baz;
} foo_int_t;

foo_t foo_init(int z)
{
   foo_int_t* rv=0;
   try
   {   
       rv=new foo_int_t;
       char *p=malloc(14);  // имитируем необходимость в динамической памяти,
                                // забыли проверить на 0
       rv->bar=bar_init();
       bar_setmem(p);      // реально если p требуется ...
       free(p); 
       rv->baz=baz_init();
       return rv;
    }
    catch (bar_init_ex &)
    {
        delete rv;
    }
    catch (baz_init_ex &)
    {
        bar_release(rv->bar);
        delete rv;
    }
    catch (std::bad_alloc &)
    {

    }
    return 0;
}

ты бы лучше словами обьяснил чем с++ хуже. мне вот интересно
как по мне большие проекты на c писать сложнее.

----------


## pal

> на двух тачках с разными архитектурами.
> gcc version 3.4.4 20050526
> gcc version 4.1.0
> 
> x.cc:6: error: call of overloaded 'abs(int&)' is ambiguous
> /usr/include/c++/4.1.0/cmath:89: note: candidates are: double std::abs(double)
> /usr/include/c++/4.1.0/cmath:93: note:                 float std::abs(float)
> /usr/include/c++/4.1.0/cmath:97: note:                 long double std::abs(long double)


 мне кажется, вы бы могли порадоваться такой обратной совместимости. std::abs(double) находится в <cmath>, т.к. fabs находится в <math.h>, а std::abs(int) находятся в <cstdlib>, т.к. abs находится в <stdlib.h>. а также сообщению об ошибке - с просто проглотил бы и вызвал неправильную функцию



> какой void*? человек видит transglukator_t.


 typedef он тоже видит в заголовке.



> нету void*. человек делает вызов trasglukator_t t=transglukator_init(); Зачем его передавать потом в bukazoid_destroy ?


 потому что этот t мало чем отличается от соседнего tt ?



> что мешает программисту взять кубический корень из windows handle? Очевидно здравый смысл.


 ничего не мешает, это то и плохо. у класса апи определен четко, а с интом можно делать что угодно.



> стандартный вариант компиляции - ошибка. компиляция с взведенным флагом - ворнинг. компиляция с флагом совместимости - без сообщений. Все, больше ничего не надо и время нас рассудило бы. Может через 20 лет лишние флаги бы умерли за ненадобностью.


 20 лет уже прошло 
я более, чем уверен, что так оно и было, просто понадобилось гораздо меньше 20 лет



> У меня CXXFLAGS установлены в одно, у кого-то - в другое значение. Все довольны.
> Расскажите, почему этого нельзя было сделать. Почему я могу компилировать K&R программы многолетней давности на С и немогу это делать на С++. Нарушение обратной совместимости - имхо признак дикой неряшливости дизайна.


 k&r - не стандарт, так что очень трудно быть уверенным в возможности его компилировать. в частности, gcc его не поддерживает.



> И надо очень сильно молиться на систему типов, чтобы поставить ее выше в своей системе ценностей.


 дык, система типов - краеугольный камень.



> Ознакомьтесь с историей и целями создания С и UNIX. С - первый язык высокого уровня, который позволил эффективно писать низкоуровневые блоки. До С все ядра ОС писались на ассемблере и портируемость ОС ограничивалась двумя-тремя архитектурами. Вместе С была создана первая коммерческая ОС полностью написанная на машинно-независимом языке. Эта ОС (UNIX) живет до сих пор. Ничего близкого С++ не создавал и не создаст. Нет задач, которые может решить только С++. Были задачи которые смог решить только С.


 By 1973, the C language had become powerful enough that most of the Unix kernel, originally written in PDP-11 assembly language, was rewritten in C. This was one of the first operating system kernels implemented in a language other than assembly. (Earlier instances include the Multics system (written in PL/I), and MCP (Master Control Program) for the Burroughs B5000 written in ALGOL in 1961.)



> Они есть и сейчас, никто не мешает написать свой драйвер ядра на С++. Но почему-то желающих нет. А если находятся, то не находятся желающие этим драйвером пользоваться.


 beos как-то написали, а сдох он совсем не из-за выбора языка. все виндовсы с 95 написаны на с++, правда, неизвестно, на какую глубину. и у них это плохо получается 
в общем, на ура пишут на с++ и ядра и вообще крупные проекты



> Удобство - критерий субъективный. Мне неудобнее. Программисту, которому на выбор надо выучить с нуля С и написать на нем проект, либо программисту который должен с нуля выучить С++ + ООП + все аббревиатуры + все шаблоны + как правильно писать (это ведь нетривиальная задача  неудобнее.


 вы совершенно зря полагаете, что с++ сильно сложнее изучить, чем с. объем стандарта даже не в два раза больше. и меньше, чем объем с#, который якобы удобный. стандартную библиотеку надо изучать, но в с тоже есть библиотека, а все, чего в ней нет, надо делать самому. и пользоваться библиотекой с++ на порядок легче. более того, основная сложность лежит в библиотеках прикладной области, объем которых на порядки больше, чем стандартных. и опять же, с++ библиотеками пользоваться легче.



> Програмисту, который матюкаясь читает код, переполненный перегруженными функциями и операторами


 ну, если там перегружено больше, чем надо, то надо ругать автора, а если столько, сколько надо, то на с то же самое выглядело бы еще хуже и содержало бы больше ошибок.



> и 10 (! сам видел) уровнями наследования - неудобнее, но это он матюкается пока не сел за отладчик, вот тут он поймет, что такое неудобно.


 если бы то же самое сделали с помощью glib/gobject, стало бы легче ? с++ всего-лишь сделал наследование более удобным в применении, наследовать можно было вручную и раньше.



> Руководству eBay в проектах которого количество членов класса превышало возможности компилятора - неудобно.


 это свидетельствует о двух вещах: кривом дизайне и кривом компиляторе



> Давайте лучше к объективным критериям вернемся.


 объективные критерии у нас уже были - ручное слежение за ресурсами - вообще непосильная задача по сравнению с raii



> Есть ли жизнь за пределами компилятора? Или лучше так - если компилятор чего-то не может, то этого не может никто
> Если для вас даже задачи статического чекинга должны выполняться компилятором, то я даже не знаю что сказать.


 а зачем делать отдельной программой то, что и так делает компилятор ?



> Есь очень много программ, работающих с кодом напрямую без компилятора. На С++ их меньше, так как у С++ усложненный синтаксис.


 и еще потому что ему их меньше нужно. сообразительные товарищи выдирают парсер из gcc



> Что убъет язык  в тот момент, когда на рынок придут функциональные языки индустриального класса.


 чисто функциональные никуда не придут, т.к. на них можно писать только калькуляторы. а грязно функциональным с++ и сам является



> Что характерно, С выживет.
> Кстати, вы с openC++ не работали?


 нет. я полагаю, что проект сдох, т.к. последний релиз 2001 года.



> если я контролирую thread pool, то мне может понадобиться массив pthread_once_t. И в этом случае он действительно удобней как int и инициализация будет в цикле.


 я начинаю опасаться, что вы не знаете, что такое pthread_once_t. ему совершенно все равно пул у вас или нет, он нужен, чтобы выполнить глобальное действие только один раз, независимо от числа потоков, пытающихся его выполнить. если вы хотите выполнить по действию на поток, то pthread_once_t вам не нужен. кстати, в с++ он не нужен вообще, т.к. инициализация статических объектов и так thread-safe.



> А вот массив cond_t либо condattr_t никому не нужен, обычно это один объект, на который ссылаются другие.


 казалось бы, при чем тут массив ?



> Хотя конечно, при работе с pthread эффективность всегда на первом месте, удобство пользования все-таки есть, но это не удобство в понимании объектников (пусть придет мама (необъектная) и сделает мне хороший компилятор, который все сделает за меня), это аскетическая простота использования и минимализм.
> pthread не относится напрямую к glibc, я его просто с потолка выбрал, по причине простоты. А вообще glibc - относительно старая библиотека с феноменальной портируемостью и очень неплохой эффективностью. Т.е. в моих терминах показатели функциональности и эффективности на высоте, удобства - в приемлимых рамках.


 glibc не портируется туда, где нет gcc, да и там, где есть, портируется на полторы системы. в то же время, с каждым gcc идет стандартная библиотека с++. и с другими компиляторами тоже должна идти.



> Я не думаю, что boost при своей идеологии в состоянии повторить эту аскетичность и простоту.


 boost уже портируется на больше систем и компиляторов, чем glibc. а 10 его библиотек уже вошли в tr1 и другие на подходе в tr2 и c++0x.

----------


## pal

> обещанный пример из реальной жизни где работает С и не работает С++.


 трудно понять, что оно должно делать, т.к. половина типов и функций не описана, но я попробую пофантазировать

----------


## pal

//header
struct A;
A*newA();
void f(A*);
void deleteA(A*);
//source
struct A{
A(const char*a,int b):s(a){
v.push_back(b);
}
std::string s;
std::vector<int> v;
};
A*newA(){
return new A("123", 456);
}
void f(A*a){
std::cout<<a->s<<std::endl;
}
void deleteA(A*a){
delete a;
}
------------
эот максимально приближенный по интерфейсу вариант. я бы так не делал. прошу обратить внимание на отсутствие try/catch

----------


## Ull9

> ------------
> эот максимально приближенный по интерфейсу вариант. я бы так не делал. прошу обратить внимание на отсутствие try/catch


 да, ты не написал try/catch, но при этом, строго говоря,
ты потенциально выбрасываешь экцепшн наружу. и при этом
никого не предупредив. 
Во всяком случае твой интерфейс об этом молчит.
operator new, может выбросить эксепшн.

либо интерфейс плох, лио код.

----------


## pal

> да, ты не написал try/catch, но при этом, строго говоря,
> ты потенциально выбрасываешь экцепшн наружу. и при этом
> никого не предупредив. 
> Во всяком случае твой интерфейс об этом молчит.
> operator new, может выбросить эксепшн.
> 
> либо интерфейс плох, лио код.


 ??
у вас какое-то особое представление об обработке исключений ? конечно, я их выбрасываю, т.к. я понятия не имею, что с ними делать. а в качестве предупреждения работает отсутствие throw ( ) в прототипах.

----------


## Ull9

наверн особое.
если я работаю с интерфейсом то я должен знать какие исключения могут выбрасыватся. иначе я не знаю что мне ловить.

представте ситуацию, я работаю с ваше библиотекой. так что?
мне прикажете, каждый вызов вашей функции обрамлять
try
{ 
    foo()
}
catch (...)
{
    log ("Ups: something bad happened")
}

по ВАШЕМУ это нормальный дизайн?

----------


## homo ludens

> мне кажется, вы бы могли порадоваться такой обратной совместимости. std::abs(double) находится в <cmath>, т.к. fabs находится в <math.h>, а std::abs(int) находятся в <cstdlib>, т.к. abs находится в <stdlib.h>. а также сообщению об ошибке - с просто проглотил бы и вызвал неправильную функцию


 Вы совершенно правы, labs определена в stdlib. Я сначала хотел написать для fabs, который Вы определили. И забыл поменять include. Я редко пишу сразу на экран, и это дает о себе знать на этом форуме. Тем не менее у меня есть правило, если код с флагом -Wall дает хоть один warning - он идет в корзину. Если бы компилятор не нашел прототипа для labs он бы дал такой ворнинг. Он его не дал, следовательно с кодом все ок. Строго говоря это будет ошибкой для мультиплатформенного кода при переносе - там появятся варнинги, что послужит сигналом для доработки.
Однако в результатах это ничего не меняет - результирующее количество строк кода 1033 - что по прежнему в 15 раз меньше показателя С++. Вы делаете ночные билды всего проекта?




> By 1973, the C language had become powerful enough that most of the Unix kernel, originally written in PDP-11 assembly language, was rewritten in C. This was one of the first operating system kernels implemented in a language other than assembly. (Earlier instances include the Multics system (written in PL/I), and MCP (Master Control Program) for the Burroughs B5000 written in ALGOL in 1961.)


 и multix и MCP были более экспериментальными вещами. Жили они мало, хотя Unix действительно был во многом вдохновлен мультиксом. Как человек когда-то учивший алгол и писавший реальные программы на PL/I могу сказать, что жизнеспособность этих систем была очень ограничена.
Unix был прорывом.




> в общем, на ура пишут на с++ и ядра и вообще крупные проекты


 я ж говорю, если пишут, то никто этим не пользуется. 
Про крупные проекты на С++ я наверное заведу отдельную ветку.




> вы совершенно зря полагаете, что с++ сильно сложнее изучить, чем с.


 Да, вы правы, объем тестов на brainbench которые я сдавал по С++ был идентичен размеру на С. Но перед тем как его сдавать надо было еще пройти тесты на ООП, которые в два раза превышали сам С++. 




> ну, если там перегружено больше, чем надо, то надо ругать автора, а если столько, сколько надо, то на с то же самое выглядело бы еще хуже и содержало бы больше ошибок.


 если на С написано + то это + всегда и везде. Не выглядело бы на С хуже.




> а зачем делать отдельной программой то, что и так делает компилятор ?


 элементарное понимание Unix way. И желание сделать то, что в компиляторе не появится никогда. И независимость от умников из technical report.




> чисто функциональные никуда не придут, т.к. на них можно писать только калькуляторы. а грязно функциональным с++ и сам является


 боюсь, что вы плохо понимаете их преимущества.




> я начинаю опасаться, что вы не знаете, что такое pthread_once_t.


 The first call to pthread_once() by any thread in a process, with a given once_control, shall call the init_routine with no arguments. Subsequent calls of pthread_once() with the same once_control shall not call the init_routine. On return from pthread_once(), init_routine shall have completed. The once_control parameter shall determine whether the associated initialization routine has been called.
примеры использования на 
http://publib.boulder.ibm.com/infoce...alizations.htm

А теперь мой пример использования в массиве.



```
typedef struct
{
  void (*func)(void);
  pthread_once_t once;
} globalinit_t;

void init_subsystem1(void);
void init_subsystem2(void);
...

globalinit_t globalinits[]=
{
  {init_subsystem1,PTHREAD_ONCE_INIT},
  {init_subsystem2,PTHREAD_ONCE_INIT}
 ...
};

#define GOBALINITSIZE (sizeof(globalinits)/sizeof(globalinit_t))
```

 Я на самом деле такие вещи предпочитаю делать не через pthread_once, а через явный инициализатор. Но наверное кому-то это надо.




> glibc не портируется туда, где нет gcc, да и там, где есть, портируется на полторы системы. в то же время, с каждым gcc идет стандартная библиотека с++. и с другими компиляторами тоже должна идти.
> 
> boost уже портируется на больше систем и компиляторов, чем glibc. а 10 его библиотек уже вошли в tr1 и другие на подходе в tr2 и c++0x.


 полторы системы? гм. ну, ну. вообще-то кроме списка http://www.gnu.org/software/libc/ports.html надо помнить что glibc реализует POSIX. Который как-бы стандарт тоже не на полторы системы. И что с++ библиотеки строятся обычно поверх POSIX так как представляют собой большей частью интерфейсы к тому же POSIX. Или библиотека с++ сама реализует доступ к файловой системе или сама вычисляет гамма-функцию?
Что характерно, я не нашел списка портов boost'а на их страничке. Поиск по слову porting дал неожиданные результаты, оказалось что boost'овцы понимают под портированием несколько другие вещи - например porting to VC++ 




> ты бы лучше словами обьяснил чем с++ хуже. мне вот интересно
> как по мне большие проекты на c писать сложнее.


 Напишу с примерами кода. Просто пост большой получается.

----------


## pal

> наверн особое.
> если я работаю с интерфейсом то я должен знать какие исключения могут выбрасыватся. иначе я не знаю что мне ловить.


 во первых, вам совсем не обязательно их ловить. во вторых, если вам надо релизить ресурсы, можете ловить ...
если надо поругаться, ловите std::exception. если надо по разному реагировать на разные исключения, ловите все, что вам нужны. но только делайте это не в каждой строчке, а на самом верху, где это имеет какой-то смысл



> представте ситуацию, я работаю с ваше библиотекой. так что?
> мне прикажете, каждый вызов вашей функции обрамлять
> try
> { 
>     foo()
> }
> catch (...)
> {
>     log ("Ups: something bad happened")
> ...


 нет, это отвратительный дизайн. это инфекция из явы. у меня вообще нет ни одного try/catch, у вас же их больше, чем собственно кода. забудьте все, чему вас научила ява, и пишите программы по человечески

----------


## pal

> Вы совершенно правы, labs определена в stdlib. Я сначала хотел написать для fabs, который Вы определили. И забыл поменять include. Я редко пишу сразу на экран, и это дает о себе знать на этом форуме. Тем не менее у меня есть правило, если код с флагом -Wall дает хоть один warning - он идет в корзину.


 [11:12:43 [email protected] ~/tmp/math]$ cat e.c
#include <stdlib.h>
#include <stdio.h>
int main ( ) {
    float a = 1.5;
    a = abs ( a );
    printf ( "%f\n", a );
    long b = 1000000000000L;
    b = abs ( b );
    printf ( "%ld\n", b );
    return 0;
}
[11:13:10 [email protected] ~/tmp/math]$ gcc -Wall -o e e.c 
[11:13:13 [email protected] ~/tmp/math]$ ./e
1.000000
727379968
---------
чето ворнингов не было



> Однако в результатах это ничего не меняет - результирующее количество строк кода 1033 - что по прежнему в 15 раз меньше показателя С++. Вы делаете ночные билды всего проекта?


 не знаю, как вы считаете строки
после препроцессирования остается от
cmath 1908
math.h 1058
cstdlib 1475
stdlib.h 1339
строк. бОльшая часть добавленных 850 и 136 строк - перегруженные функции, ради которых все и затевалось



> и multix и MCP были более экспериментальными вещами. Жили они мало, хотя Unix действительно был во многом вдохновлен мультиксом. Как человек когда-то учивший алгол и писавший реальные программы на PL/I могу сказать, что жизнеспособность этих систем была очень ограничена.
> Unix был прорывом.


 unix _стал_ прорывом. а были они все одинаково экспериментальные и плохие. просто unix был открытым и сравнительно простым, что позволяет быстро размножаться



> я ж говорю, если пишут, то никто этим не пользуется.


 я вижу, что вы говорите, что виндами никто не пользуется, но это слегка не соответствует действительности. а beos был убит мелкософтом, но это уже оффтопик.



> Да, вы правы, объем тестов на brainbench которые я сдавал по С++ был идентичен размеру на С. Но перед тем как его сдавать надо было еще пройти тесты на ООП, которые в два раза превышали сам С++.


 я уже говорил о glib/gobject, там ведь тоже без ооп никуда, только помощи от компилятора никакой



> если на С написано + то это + всегда и везде. Не выглядело бы на С хуже.


 т.е. strcat не выглядит хуже + ?



> элементарное понимание Unix way. И желание сделать то, что в компиляторе не появится никогда. И независимость от умников из technical report.


 unix way не предполагает написания тысячи одинаковых парсеров. более того, он, как и ооп, не везде применим. вы много видели unix баз данных на пайпах ? про возможность использования парсера из gcc я уже писал.
про умников я не осилил



> боюсь, что вы плохо понимаете их преимущества.


 я отлично понимаю их преимущества, более того, они мне нравятся. но я также понимаю, что мир вокруг не функциональный и не все реально представить в виде функции без побочных эффектов. все более-менее используемые языки вроде лиспа и мл не чисто функциональные. как и с++



> А теперь мой пример использования в массиве.
> 
> 
> 
> ```
> typedef struct
> {
>   void (*func)(void);
>   pthread_once_t once;
> ...


 я так понимаю, что если у вас массив, то вы собрались по нему в цикле проходить. в таком случае вам нужен один pthread_once_t и одна функция, которая пройдет по массиву инициализаторов и все их вызовет.



> полторы системы? гм. ну, ну. вообще-то кроме списка http://www.gnu.org/software/libc/ports.html


 linux - 1 система, hurd - пол системы 



> надо помнить что glibc реализует POSIX. Который как-бы стандарт тоже не на полторы системы.


 но это не относится к портируемости glibc



> И что с++ библиотеки строятся обычно поверх POSIX так как представляют собой большей частью интерфейсы к тому же POSIX. Или библиотека с++ сама реализует доступ к файловой системе или сама вычисляет гамма-функцию?


 с++ библиотеки строятся и поверх posix и поверх win32 и не только. стандартная библиотека с++ - тоже стандарт, реализованный на большем числе систем, чем posix.



> Что характерно, я не нашел списка портов boost'а на их страничке. Поиск по слову porting дал неожиданные результаты, оказалось что boost'овцы понимают под портированием несколько другие вещи - например porting to VC++


 http://boost.org/more/getting_started.html#Tools

----------


## Ull9

> во первых, вам совсем не обязательно их ловить. во вторых, если вам надо релизить ресурсы, можете ловить ...


 Ну, давайте по порядку. Во первых, если я непоймаю эксепшн, то ... у меня наступит краш программы. и никакого во вторых уже ненаступит, все... точка, моего процесса нет. Причем тут ресурсы? все! тю-тю. Баржоми непоможет. 
далее. ловить std::exception тоже непоможет. а что если либина выбросит допустим std::string? как у меня уже как то и было? А автор либины находится в сингапуре? Вот красота!!!

Хороший интерфейс не содержит сюрпризов. не должен. не должен бросать незадекларированных эксепшнов (ваш случай) наружу.




> если надо по разному реагировать на разные исключения, ловите все, что вам нужны. но только делайте это не в каждой строчке, а на самом верху, где это имеет какой-то смысл.


 Тут я скажу так, смысл иногда есть ловить эксепшн и внутри тоже, а иногда и вверху. тут все поразному. универс. правила нет.

А что касается моуг трай кэтч, то... дурная постановка задачи, дурной неясный пример. вот оно и получилось так.
я лично вообще предпочитаю не работать с эксепшнами. Но такова была постановка задачи. 
Еще раз. было написано 
проверять malloc на ноль. это С 
ловить эксепшн........... это С++

У вас эксепшн не ловится.

----------


## homo ludens

> [11:13:10 [email protected] ~/tmp/math]$ gcc -Wall -o e e.c 
> чето ворнингов не было


 обычное переполнение. В чем проблема.
int16_t x=32767;
x+x+x вам даст неожиданный результат что в С что в С++. long и int взаимозаменяемы. Компилятор выдаст ворнинг если допустим сравнение знакового и беззнакового и иногда это полезно. Опять таки labs юзать надо, если ожидаешь таких значений. Не ожидаешь - не юзай.



> не знаю, как вы считаете строки


 <algorithm> забыли. 





> я вижу, что вы говорите, что виндами никто не пользуется, но это слегка не соответствует действительности. а beos был убит мелкософтом, но это уже оффтопик.


 Странно почему они не убили Линукс. : -)
Я не лазил внутрь винды давно, но HWND вроде был интом? 




> я уже говорил о glib/gobject, там ведь тоже без ооп никуда, только помощи от компилятора никакой


 Потому и не юзаю glib.
А про ГУИ я уже писал - это та точка, где ООП необходим, однако и это является проблемой дизайна, а не программиста.




> т.е. strcat не выглядит хуже + ?


 нет конечно. он несколько менее компактен, но он всегда однозначно идентифицирует действие и типы аргументов.  А переопределяя операторы вы всегда придете к проблеме yo-yo.




> unix way не предполагает написания тысячи одинаковых парсеров.


 нет, он предполагает максимальное использование стандартных библиотек пока есть возможность и написание собственной библиотеки, расширяющей эту возможность когда тупик.
gcc парсер неуниверсален и есть ряд задач, где он не катит, я знаю о чем говорю, так как одна из таких задач сейчас стоит передо мной как потенциальный заказ. Анализ проводился.



> более того, он, как и ооп, не везде применим. вы много видели unix баз данных на пайпах ?


 Unix базы делаются не на пайпах а на сокетах (в т.ч. и локальных) и shared memory. 
Встречный вопрос, вы много видели объектно-ориентированных баз данных? Вы вообще их видели? История ООП баз напоминает бразильский сериал под названием "Убитое время между вторым и третьим манифестом или почему был продан Информикс". : -)  Стандарт ODMG если вам это неизвестно накрылся медным тазом, в результате чего высказывание, что все крупные проекты пишутся на ООП становится в высшей степени сомнительным. Если проект крупный - то он использует базу данных. Если он объектный - то очевидно объектную. А таковых как известно нет, есть только миллиарды долларов убытка.
ORM технологии в виде маленькой инвалидной коляски прицепленной на нижний конец каждого костыля не предлагать. : -)




> я отлично понимаю их преимущества, более того, они мне нравятся. но я также понимаю, что мир вокруг не функциональный и не все реально представить в виде функции без побочных эффектов. все более-менее используемые языки вроде лиспа и мл не чисто функциональные. как и с++


 Представьте себе автоматический генератор кода по спецификации на SKI-комбинаторах. Я думаю при появлении такой вещи половина программистов мира уйдут искать работу. И это только часть айсберга, потому что достаточность лямбда исчисления и комбинаторного исчисления для описания мира, в отличие от ООП является математическим фактом. Мир вокруг не объектный - он функциональный. Вас кто-то обманул.




> я так понимаю, что если у вас массив, то вы собрались по нему в цикле проходить. в таком случае вам нужен один pthread_once_t и одна функция, которая пройдет по массиву инициализаторов и все их вызовет.


 нет, потому что это повышает связность программы и для того чтобы ввести подсистему или вывести из кода мне хватит #ifdef SUBSYSTEM1, а в вашей реализации вам потребуется править код функции. Это не означает что ваш вариант неправильный, это означает что вы берете на себя ответственность сопровождать код одной функции инициализации, а я - нет. личный выбор.




> linux - 1 система, hurd - пол системы


 дайте поиск по слову glibc+Darwin или glibc+Irix или еще что-то.




> стандартная библиотека с++ - тоже стандарт, реализованный на большем числе систем, чем posix.
> 
> http://boost.org/more/getting_started.html#Tools


 так я не понял, там своя реализация гамма-функции или нет? : -)
По ссылке на boost видно что я был прав. Там огромный список портов, включающих в себя такие сверхважные для человечества платформы (!) как Borland C++ и Kylix. : -) Это действительно круто.
Кроме линукса и Hurd glibc ставится на кучу систем. Обычно это непрактично, но фанаты найдутся. Дайте поиск по инету по glibc+Darwin или glibc+IRIX. Много интересного найдете. По поводу того, что библиотека C++ более портируема чем Portable Operating System Interface (POSIX) которого придерживается даже винда я тоже не хочу спорить. Пока что boost портируется не под платформы - под компиляторы!

----------


## Ull9

прошу прощения, что вмешиваюсь в высокий спор, но хочу сказать свою точку зрения.

с/с++. Писал и на первом, писал и на втором. Сейчас поддерживаю продукт, где большой кусок наисан на с, и еще больший на с++. на с писать большие проекты тяжелей, тежелей выявлять взаимосвязи. без обектов, тэжело реализовывать бизнес логику.
Вам Homo ludens могу сказать, что кроме пафоса и мельтешения от перечисления технологий у вас ничего нет. нашел также немало мест где просто логика страдает.

типа здесь
<<Если проект крупный - то он использует базу данных. Если он объектный - то очевидно объектную. А таковых как известно нет, есть только миллиарды долларов убытка.
>>
С какой стати ооп код ДОЛЖЕН пользовать обьектную базу? да мне вообще все равно как написана БД, есть с ней интерфейс, и все. если надо напишу ооп оболочку к интерфейсу. Нет? буду пользоватся С интерфейсом. 

На С писать можно, можно написать что угодно, также как и на ассемблере. Ну ичто?
на С++ это пишется быстрее, легче, при небольших потерях скорости.

----------


## homo ludens

блин, наконец то добрался до обещанного кода.
Хотел все сделать только кодом но наверно это будет долго. 
Итак гм. преамбула. 
Мы берем тот неряшливый код с ошибками, который я привел выше. И сравниваем с тем кодом, который привел pal с поправками Ull9.
Первый код глючен и неряшлив. Второй красив и гарантирует освобождение памяти. С этим согласны все и даже я. 
Кроме того, если кто-то хочет сказать, что компилятор С не отлавливает ошибки программиста, а С++ - отлавливает, то я с ним спорить не буду. Потому что с моей точки зрения компилятор должен компилировать. А отлавливатель ошибок - отлавливать ошибки. Поэтому мне пофиг все ошибки динамической памяти. Просто пофиг. Пусть об этом заботятся люди, которые жалуются на то, что отвертка плохо забивает гвозди. Первым делом после компиляции (и предшествующего статического чекинга ибо никакой компилятор этого не заменит) я запускаю программу по отлову динамических ошибок памяти. Она называется valgrind. Очень рекомендую всем, кто ей никогда не пользовался. valgrind мне покажет все проблемы с динамической памятью, которые возникли при выполнении программы. Я их правлю.
Следующий этап. 
vg показал мне все ошибки с ДП которые были при выполнении. Но он не покажет те ошибки которые могут возникнуть. Для того, чтобы он показал нам их мы делаем вот такой трюк. Блок кода (или весь файл) который мы хотим исследовать на потенциальные ошибки ДП мы заключаем в следующие скобки:

#define if(x)	if(if_handler(x, __func__,__FILE__,__LINE__))
...
#undef if

Что делает if_handler? Да что угодно. Например если мы дадим return (rand()%2) || x; то получим тестирование методом Монте-Карло. Более практичным является методика, когда эта функция перебирает или генерирует нужные тестовые последовательности, нужные для гарантированного покрытия всего множества возможных ветвлений. Алгоритм давать надеюсь не надо. :- )
После этого гоняем прогу под valgrindom с разными последовательностями, а пока она так гоняется, топчем клаву здесь на форуме. Это пока жители Виллабаджо пишут юнит-тесты и изучают RAII. 

Метод с переопределением if на самом деле не найдет всех ошибок памяти. Для полного перебора надо еще переопредлять malloc - на тот случай если где-то он (или функция которая его вызывает) вернет 0 и проверки этого if-ом нет в коде. Делается по похожей схеме, но уже не макросами, что создает намного больше проблем, но тоже решается.

Что еще может не поймать такая схема? Практически ничего. Например, выделение памяти внутри цикла, который выполняется 0 раз при любой тестовой последовательности. Приведенными выше средствами это не лечится, но это можно отследить вручную. Либо использовать артилерию потяжелее. Я предпочитаю вручную.

Что не может такая схема? Быть реализованной на С++. И не только из-за переусложненного синтаксиса, но еще из-за того, что глюки и исключения, которые возникают в данном блоке кода размазаны по всей программе, а в моем примере в С - все проверки сконцентрированы внутри блока, который мы проверяем.

Вот собственно и обещанный пример...

PS в реальной жизни юзаю чуть другую схему, с m4 плюс еще несколько фич. Но и эта вполне рабочая.

2 Ull9
Крупный ООП проект на С++ это проект в котором сетевые интерфейсы и базы данных объектными не являются, а объектной является лишь бизнес-логика?
Я правильно понял?

----------


## pal

> Ну, давайте по порядку. Во первых, если я непоймаю эксепшн, то ... у меня наступит краш программы.


 если программа не поймает. совсем необязательно его ловить вам и совсем необязательно в каждой строчке.



> далее. ловить std::exception тоже непоможет. а что если либина выбросит допустим std::string? как у меня уже как то и было? А автор либины находится в сингапуре? Вот красота!!!


 я же сказал ловите ...
и не бросайте std::string, а если бросаете, то документируйте это.



> Хороший интерфейс не содержит сюрпризов. не должен. не должен бросать незадекларированных эксепшнов (ваш случай) наружу.


 нет никаких сюрпризов, в интерфейсе четко указано, что могут бросаться любые исключения. или вы в каком-то из прототипов throw ( ) видите ?



> Тут я скажу так, смысл иногда есть ловить эксепшн и внутри тоже, а иногда и вверху. тут все поразному. универс. правила нет.


 иногда все по разному. но у нас конкретный пример, в котором вы, не имея на то никаких оснований, зачем-то проглотили все исключения, совсем не зная, зачем они нужны и что с ними делать. а если это исключение предназначено не вам ?



> А что касается моуг трай кэтч, то... дурная постановка задачи, дурной неясный пример. вот оно и получилось так.
> я лично вообще предпочитаю не работать с эксепшнами. Но такова была постановка задачи.


 не работать с исключениями не получится, если пользоваться стандартным с++



> Еще раз. было написано 
> проверять malloc на ноль. это С 
> ловить эксепшн........... это С++


 не правильный ответ. с++ - это при возникновении исключения либо не ломаться, либо откатывать транзакцию. ловить руками при этом приходится крайне редко, при условии, что вы знаете, что делаете



> У вас эксепшн не ловится.


 а зачем мне это ? у меня ресурсы не текут и объекты в неработоспособном состоянии не остаются. или вы видите что-то, чего не вижу я ?

----------


## TechInsightJobs

> А отлавливатель ошибок - отлавливать ошибки. Поэтому мне пофиг все ошибки динамической памяти. Просто пофиг. Пусть об этом заботятся люди, которые жалуются на то, что отвертка плохо забивает гвозди. Первым делом после компиляции (и предшествующего статического чекинга ибо никакой компилятор этого не заменит) я запускаю программу по отлову динамических ошибок памяти. Она называется valgrind. Очень рекомендую всем, кто ей никогда не пользовался. valgrind мне покажет все проблемы с динамической памятью, которые возникли при выполнении программы. Я их правлю.


 
счастливы люди пишущие раз за разом программы не сложнее хеллоу ворлд

----------


## Ull9

> блин, наконец то добрался до обещанного кода.
> Кроме того, если кто-то хочет сказать, что компилятор С не отлавливает ошибки программиста, а С++ - отлавливает, то я с ним спорить не буду. Потому что с моей точки зрения компилятор должен компилировать. А отлавливатель ошибок - отлавливать ошибки. Поэтому мне пофиг все ошибки динамической памяти. Просто пофиг. Пусть об этом заботятся люди, которые жалуются на то, что отвертка плохо забивает гвозди. Первым делом после компиляции (и предшествующего статического чекинга ибо никакой компилятор этого не заменит) я запускаю программу по отлову динамических ошибок памяти. Она называется valgrind. Очень рекомендую всем, кто ей никогда не пользовался. valgrind мне покажет все проблемы с динамической памятью, которые возникли при выполнении программы. Я их правлю.


 Компилятор должен компилировать. это так, Но если он еще и может отлавливать ошибки логики разве это плохо? Разве плохо, что он поможет предотвратить ошибли бизнесс-логики? Краш? О чем спор? ЗАЧЕМ ОТКАЗЫВАТСЯ ОТ ТОГО ЧТО ДАЕТ БЕСПЛАТНО ЛЮБОЙ КОМПИЛЯТОР С++?

Теперь по поводу твоего тула. Ну а если нет у меня этого тула? на моей платформе его нет вообще. AIX. И не говори мне про другие тулы. нельзя мне ничего ставить на машину заказчика. Дальше что? Что мне делать с твоими советами?
Надо пользоватся компилятором с++. его вполне хватит чтоб писать правильные проги.




> 2 Ull9
> Крупный ООП проект на С++ это проект в котором сетевые интерфейсы и базы данных объектными не являются, а объектной является лишь бизнес-логика?
> Я правильно понял?


 Нет, неправильно. Крупный проект ООП, это проэкт который использует длй решения задачи ооп подход, не только бизнесс логика, но и вся логика выражена через ООП.

----------


## pal

> обычное переполнение. В чем проблема.
> int16_t x=32767;
> x+x+x вам даст неожиданный результат что в С что в С++. long и int взаимозаменяемы. Компилятор выдаст ворнинг если допустим сравнение знакового и беззнакового и иногда это полезно. Опять таки labs юзать надо, если ожидаешь таких значений. Не ожидаешь - не юзай.


 это не обычное переполнение. у меня лонг 64 бит, и abs даже не поругался, что я ему даю вместо 32 бит 64. или число с плавающей точкой. а в с++, если вы забыли подключить заголовок, то ошибка, а если не забыли, то вызовется правильная функция. вот вам и система типов и перегруженные функции



> <algorithm> забыли.


 не более, чем /usr/include/*
зачем мне подключать <algorithm>, если нужные мне функции определены в <cmath> или <cstdlib> ?
интересный подход



> Странно почему они не убили Линукс. : -)


 у линукса трудноубиваемая модель разработки. простуду ведь тоже не побороли 



> Я не лазил внутрь винды давно, но HWND вроде был интом?


 не знаю. если вы о с апи, то без него ведь на с программы под винды писать не получилось бы.



> Потому и не юзаю glib.


 ну я полагаю, что те, кто юзает, делает это не назло кондуктору



> А про ГУИ я уже писал - это та точка, где ООП необходим, однако и это является проблемой дизайна, а не программиста.


 учитывая, что там ооп у всех, это скорее является особенностью прикладной области, а не проблемой



> нет конечно. он несколько менее компактен, но он всегда однозначно идентифицирует действие и типы аргументов.  А переопределяя операторы вы всегда придете к проблеме yo-yo.


 специально для вас имеется в наличии std::string::append, все остальные могут использовать более интуитивный +



> нет, он предполагает максимальное использование стандартных библиотек пока есть возможность и написание собственной библиотеки, расширяющей эту возможность когда тупик.
> gcc парсер неуниверсален и есть ряд задач, где он не катит, я знаю о чем говорю, так как одна из таких задач сейчас стоит передо мной как потенциальный заказ. Анализ проводился.


 в таком случае, с++, как язык с мощными средствами для создания библиотек, является ярким примером unix way.
однако, непонятно, как более сложный синтаксис с++ делает его менее приспособленным для unix way. как раз наоборот, больше стимул сделать одну библиотеку вместо тысячи по своему кривых парсеров на коленке.
вы можете взять другой парсер, или в конце концов сделать самую правильную библиотеку



> Unix базы делаются не на пайпах а на сокетах (в т.ч. и локальных) и shared memory.


 и что, в эти сокеты вставляются grep, sort и т.п. ? 



> Встречный вопрос, вы много видели объектно-ориентированных баз данных? Вы вообще их видели?


 я всего видел две базы данных. mysql написан на с++, postgresql сам себя называет object-relational dbms.



> Если проект крупный - то он использует базу данных. Если он объектный - то очевидно объектную.


 не очевидно. если нужна реляционная, то используется реляционная. мне пока другой надо не было



> Представьте себе автоматический генератор кода по спецификации на SKI-комбинаторах. Я думаю при появлении такой вещи половина программистов мира уйдут искать работу.


 этот миф развенчан более 30 лет назад. основная сложность разработки по ложится на дизайн, а его сильно легче не сделать никаким языком и никакими генераторами кода. почитайте брукса



> И это только часть айсберга, потому что достаточность лямбда исчисления и комбинаторного исчисления для описания мира, в отличие от ООП является математическим фактом. Мир вокруг не объектный - он функциональный. Вас кто-то обманул.


 вы наверное первый человек, который не видит в окружающем мире побочных эффектов 
как бы вам объяснить. вы пробовали файлы с диска удалять ? функциональный подход - сделать новый диск без лишних файлов. удачи 



> нет, потому что это повышает связность программы и для того чтобы ввести подсистему или вывести из кода мне хватит #ifdef SUBSYSTEM1, а в вашей реализации вам потребуется править код функции. Это не означает что ваш вариант неправильный, это означает что вы берете на себя ответственность сопровождать код одной функции инициализации, а я - нет. личный выбор.


 не правильный ответ. массив то у вас общий все равно, вот и ставьте ifdef вокруг его элементов



> дайте поиск по слову glibc+Darwin или glibc+Irix или еще что-то.


 This project is a stab at providing a port of the GNU c library (glibc) to Apple's new Darwin platform, the nix-based platform for OS X. релизов нет. про irix вообще непонятно, я полагаю, что у них есть эмуляция linux, как в freebsd. и к чему это все ? glibc содержит код для полутора систем как и раньше.



> так я не понял, там своя реализация гамма-функции или нет? : -)


 я не совсем понимаю, чего вы добиваетесь. в стандарт с++ она вообще не входит. в tr1 появились lgamma и tgamma. стандарт не определяет, своя должна быть реализация или нет. в gcc 4.1 их еще нет, в svn реализации выглядят примерно так:
  using ::lgamma;
...
   inline float
  lgamma(float __x)
  { return __builtin_lgammaf(__x); }

  inline long double
  lgamma(long double __x)
  { return __builtin_lgammal(__x); }



> По ссылке на boost видно что я был прав. Там огромный список портов, включающих в себя такие сверхважные для человечества платформы (!) как Borland C++ и Kylix. : -) Это действительно круто.


 видно совсем наоборот. это список того, что поддерживает тарболл, который вы скачаете. в список входят все системы, поддерживаемые glibc



> Кроме линукса и Hurd glibc ставится на кучу систем. Обычно это непрактично, но фанаты найдутся.


 фанаты могут делать что им нравится. glibc поддерживает полторы системы и один компилятор. boost поддерживает все то же самое плюс много больше.



> По поводу того, что библиотека C++ более портируема чем Portable Operating System Interface (POSIX) которого придерживается даже винда я тоже не хочу спорить.


 и не спорьте. эта библиотека на ура соберется под этим самым посиксом под виндой. не говоря уже о том, что вы пытаетесь сравнить портируемость _реализации_ с портируемостью _интерфейса_.



> Пока что boost портируется не под платформы - под компиляторы!


 т.е. msvc, sunpro и tru64cxx - это все компиляторы под linux или hurd  ?

----------


## Ull9

> если программа не поймает. совсем необязательно его ловить вам и совсем необязательно в каждой строчке.


 если я непоймаю какой-либо эксепшн, это означает что он вылетит наружу. А я обязан выбрасывать только мои эксепшны. любой другой эксепшн я должен преобразовывать в свой. вышестоящий модуль может ловить ТОЛьКО нои эксепшны.
Возникает, непойманный эксепшн. дальше креш. Приехали.




> я же сказал ловите ...
> и не бросайте std::string, а если бросаете, то документируйте это.
> нет никаких сюрпризов, в интерфейсе четко указано, что могут бросаться любые исключения. или вы в каком-то из прототипов throw ( ) видите ?


 Вот тот умник, который бросал string, он рассуждал ТОЧНО ТАК ЖЕ КАК ТЫ. 
Он говорил, примерно так: Ну я же нигде неписал throw (). Т.е как бы он задокументировал в коде, что бросаю любые обьекты. Так что же вы хотите?
Пишите catch (...)... Вы с ним случаем не в одной школе учились?




> иногда все по разному. но у нас конкретный пример, в котором вы, не имея на то никаких оснований, зачем-то проглотили все исключения, совсем не зная, зачем они нужны и что с ними делать. а если это исключение предназначено не вам ?


 Я ушодил из того что данный код написан на С, Из этого сделал вывод, исключения бросать ненадо. Т.к вполне вероятно что вызывающий код написан не на с++.
Пример дурной.




> не работать с исключениями не получится, если пользоваться стандартным с++


 Вполне получится, пишите например new (std::nothrow).




> а зачем мне это ? у меня ресурсы не текут и объекты в неработоспособном состоянии не остаются. или вы видите что-то, чего не вижу я ?


 Да но бросая, эксепшн непредупредив, бы лонаете мои ресурсы, мой код становится в неработоспособном состоянии. 
И не надо мне говорить, что ненаписав throw(), ты меня предупредил.
из-за такого умника, который меня так "предупредил", код имел уродливый
catch(...). И главное он так и непонял, почему его уволили. все бубнил, что
раз ненаписано throw(), it means alles ok.

----------


## pal

> Потому что с моей точки зрения компилятор должен компилировать. А отлавливатель ошибок - отлавливать ошибки. Поэтому мне пофиг все ошибки динамической памяти. Просто пофиг. Пусть об этом заботятся люди, которые жалуются на то, что отвертка плохо забивает гвозди.


 непонятно, почему тогда вы пропагандируете функциональные языки. ведь единственное их преимущество в том, что правильность чисто функциональной программы можно доказать математически.



> Первым делом после компиляции (и предшествующего статического чекинга ибо никакой компилятор этого не заменит) я запускаю программу по отлову динамических ошибок памяти. Она называется valgrind. Очень рекомендую всем, кто ей никогда не пользовался. valgrind мне покажет все проблемы с динамической памятью, которые возникли при выполнении программы. Я их правлю.


 valgrind это отлично, но лучше не допускать ошибок, чем лихо их исправлять. к тому же, как вы верно заметили ниже, он не найдет ошибок, которых не было в этом запуске



> Следующий этап. 
> vg показал мне все ошибки с ДП которые были при выполнении. Но он не покажет те ошибки которые могут возникнуть. Для того, чтобы он показал нам их мы делаем вот такой трюк. Блок кода (или весь файл) который мы хотим исследовать на потенциальные ошибки ДП мы заключаем в следующие скобки:
> 
> #define if(x)	if(if_handler(x, __func__,__FILE__,__LINE__))
> ...
> #undef if
> 
> Что делает if_handler? Да что угодно. Например если мы дадим return (rand()%2) || x; то получим тестирование методом Монте-Карло. Более практичным является методика, когда эта функция перебирает или генерирует нужные тестовые последовательности, нужные для гарантированного покрытия всего множества возможных ветвлений. Алгоритм давать надеюсь не надо. :- )
> После этого гоняем прогу под valgrindom с разными последовательностями, а пока она так гоняется, топчем клаву здесь на форуме. Это пока жители Виллабаджо пишут юнит-тесты и изучают RAII.


 ну, это просто не смешно. если у вас есть 1000 условий, приводящих к завершению программы, то сколько раз ее надо запустить, чтобы таким методом пройти дальше всех этих условий и проверить что-то после них ?
я уж не говорю о таком маразме, как во что превратится
int abs ( int a ) {
if(a<0) return -a;
return a;
}
или о том, что это не работает с условиями в выражениях, без оператора if. т.е. return is_error()?false:more_work();



> Метод с переопределением if на самом деле не найдет всех ошибок памяти. Для полного перебора надо еще переопредлять malloc - на тот случай если где-то он (или функция которая его вызывает) вернет 0 и проверки этого if-ом нет в коде. Делается по похожей схеме, но уже не макросами, что создает намного больше проблем, но тоже решается.


 вам придется переопределять не только маллок, но и все остальные функции, возвращающие код ошибки. т.е. практически все функции в программе, не использующей исключения



> Что не может такая схема? Быть реализованной на С++. И не только из-за переусложненного синтаксиса, но еще из-за того, что глюки и исключения, которые возникают в данном блоке кода размазаны по всей программе, а в моем примере в С - все проверки сконцентрированы внутри блока, который мы проверяем.


 видимо, имелось в виду не "на с++", а "в программе, использующей исключения". это не так, вам все равно надо перекомпилировать все, включая библиотеки, так что можно вставить аналогичные макросы в места, бросающие исключения. и работать это будет правильннее



> Вот собственно и обещанный пример...


 это просто антипример получился

----------


## pal

> если я непоймаю какой-либо эксепшн, это означает что он вылетит наружу. А я обязан выбрасывать только мои эксепшны. любой другой эксепшн я должен преобразовывать в свой. вышестоящий модуль может ловить ТОЛьКО нои эксепшны.
> Возникает, непойманный эксепшн. дальше креш. Приехали.


 кто вам сказал такую глупость ? исключение, с которым неизвестно, что делать, обязано беспрепятственно выходить наружу. причем, в неизмененном виде. преобразовывать исключения - это вообще неописуемо.
вышестоящий модуль может ловить std::exception или ..., а может и не ловить, если ему это не интересно. откуда мы вообще что-то знаем о _вышестоящем_ модуле ??
непойманное исключение может возникнуть только в сАмом вышестоящем модуле, т.е. в main в однопоточном приложении. если там его не поймали, то значит завершение программы представляется подходящим действием. в чем проблемы ?



> Вот тот умник, который бросал string, он рассуждал ТОЧНО ТАК ЖЕ КАК ТЫ. 
> Он говорил, примерно так: Ну я же нигде неписал throw (). Т.е как бы он задокументировал в коде, что бросаю любые обьекты. Так что же вы хотите?
> Пишите catch (...)... Вы с ним случаем не в одной школе учились?


 ну, он был в праве бросать все, что угодно. но бросать не потомок std::exception - плохой стиль.
я в прошлом посте сказал не бросать, а если бросать, то документировать. в моем примере я _ничего_ не бросаю. бросают std::string и std::vector, остается рассчитывать на их вменяемость. а вы что предлагаете мне делать ?
1 - ловить ... и бросать что-то взамен ? занимайтесь извращениями в main и оставьте в покое мой правильный модуль
2 - ловить ... и ничего не бросать ? а что делать ? возвращать код ошибки ? - пишите на с и не мешайте 



> Я ушодил из того что данный код написан на С, Из этого сделал вывод, исключения бросать ненадо. Т.к вполне вероятно что вызывающий код написан не на с++.
> Пример дурной.


 вызывающий код не на с++ не сможет вызвать ваши функции, т.к. они не объявлены extern "C".



> Вполне получится, пишите например new (std::nothrow).


 не получится, вся стандартная библиотека бросает исключения. замена стандартной библиотеки не подходит под определение "писать на стандартном с++"



> Да но бросая, эксепшн непредупредив, бы лонаете мои ресурсы, мой код становится в неработоспособном состоянии. 
> И не надо мне говорить, что ненаписав throw(), ты меня предупредил.
> из-за такого умника, который меня так "предупредил", код имел уродливый
> catch(...). И главное он так и непонял, почему его уволили. все бубнил, что
> раз ненаписано throw(), it means alles ok.


 lol ?
смотрим в std::string
basic_string(const basic_string& __str);
с аллокатором по умолчанию она точно может бросать std::bad_alloc.
уволить c++ standards committee ? 
прошу обратить внимание, у меня такое же объявление и такие же бросаемые исключения.
уволить надо вас, если функция, не объявленная throw() ломает ваши ресурсы, и ваш код становится в неработоспособном состоянии.
а вот у меня не сломалось ни одного ресурса и код работоспособен, и мне совершенно не надо знать, что бросают std::string и std::vector
я думаю, это потому, что я знаю, как надо работать с исключениями в с++

----------


## Ull9

> кто вам сказал такую глупость ? исключение, с которым неизвестно, что делать, обязано беспрепятственно выходить наружу. причем, в неизмененном виде. преобразовывать исключения - это вообще неописуемо.


 Мне это сказал архитектор. из моего модуля недожны выпадать неописанные и непредусмотренные исключения. И я с ним согласен. вышестоящий модуль бросает свой эксепшн, некий А, но тот кто работает с моим модулем Б, понятия не имеет что работая с модулем Б, он кроме Б, должен ловить еще и А. причем искА и искБ, не имеют базового класа. И если Б выбрасывает усключение искА, все, краш.Что недопустимо, опять же архитектором.



> откуда мы вообще что-то знаем о _вышестоящем_ модуле ??


 на то есть HLD, документ который описывает, что от твоего модуля требуется, что он может бросать, а что нет.



> непойманное исключение может возникнуть только в сАмом вышестоящем модуле, т.е. в main в однопоточном приложении. если там его не поймали, то значит завершение программы представляется подходящим действием. в чем проблемы ?


  проблемы? подходяще действие, это вызов unexpected затем abort. 




> ну, он был в праве бросать все, что угодно. но бросать не потомок std::exception - плохой стиль.


  Ну будем считать что его уволили за плохой стиль, как ты выразился. хотя ты утверждаешь, что надо было уволить меня. Где же логика? 




> 1 - ловить ... и бросать что-то взамен ? занимайтесь извращениями в main и оставьте в покое мой правильный модуль
> 2 - ловить ... и ничего не бросать ? а что делать ? возвращать код ошибки ? - пишите на с и не мешайте


 Ну вот ты сам же и пишешь (пункт 1), что при твоем "правильном" подходе, как минимум один модуль, ДОЛЖЕН заниматься изврашениями
Теперь пункт 2. В постановке задачи, сказано что делать, возвращать 0. еще раз почитай пример на С. там все четко написано. Я так и зделал. ты - нет. ненадо никакой отсебятины.
В нормальной жизни тебе говорит архитектор, что делать.




> вызывающий код не на с++ не сможет вызвать ваши функции, т.к. они не объявлены extern "C".
> 
> не получится, вся стандартная библиотека бросает исключения. замена стандартной библиотеки не подходит под определение "писать на стандартном с++"
> 
> lol ?
> смотрим в std::string
> basic_string(const basic_string& __str);
> с аллокатором по умолчанию она точно может бросать std::bad_alloc.
> уволить c++ standards committee ? 
> ...


 немного уже подустал, .. да нехватает extern C, меня не уволили. т.к. компания где я работаю (ну оч. известная), придерживается другой философии программирования.
я не сонмеваюсь, что вы знаете как работать с исключениями, в конце концов никто никого не учит, во всяком случае здесь.
здесь просто увольняют

Удачи

----------


## homo ludens

> счастливы люди пишущие раз за разом программы не сложнее хеллоу ворлд


 Блаженны люди, которые не видят собственных внутренних противоречий.
Человек, который желает иметь компилятор, отлавливающий все ошибки и при этом не называется интерпретатором обречен на вечный когнитивный диссонанс. Как впрочем и человек, верящий в то, что здание ООП построенное на трех столбах (наследование, инкапсуляция, полиморфизм) будет устойчиво когда по крайней мере две из трех концепций противоречат друг другу. 




> Компилятор должен компилировать. это так, Но если он еще и может отлавливать ошибки логики разве это плохо? Разве плохо, что он поможет предотвратить ошибли бизнесс-логики? Краш? О чем спор? ЗАЧЕМ ОТКАЗЫВАТСЯ ОТ ТОГО ЧТО ДАЕТ БЕСПЛАТНО ЛЮБОЙ КОМПИЛЯТОР С++?


 Потому что кроме того, что он дает, он еще много забирает. Например возможности автоматической обработки исходного кода.
Плюс еще много чего.




> Теперь по поводу твоего тула. Ну а если нет у меня этого тула? на моей платформе его нет вообще. AIX. И не говори мне про другие тулы. нельзя мне ничего ставить на машину заказчика. Дальше что? Что мне делать с твоими советами?
> Надо пользоватся компилятором с++. его вполне хватит чтоб писать правильные проги.


 Я сам писал на HPUX, потому завидую хорошему железу и сочуствую плохому софту (блин на моей версии даже threads не было в природе).
Когда ничего нельзя ставить на машину заказчика - у разработчика ставиться аналогичная машина с идентичным софтом и версией ОС вплоть до патчей (кстати, никому не нужна PA-RISC E-шка 10-летней давности? Дома только пыль собирает : -).
Тулза здесь не главное. Тулзу можно сэмулировать malloc hooks либо просто подменой malloc.  Хотя это конечно гемор тот еще, плюс большие тормоза в отладке, но это работа одноразовая и на всю жизнь.
Главное здесь возможность синтаксического анализа исходного кода и концентрация анализа ошибок  в том блоке, где они возникают. С++ размазывает источник ошибки по всей программе, С- нет.
Опять таки, мой метод упрощен и примитивен. Даже такая софтина как http://opera.cs.uiuc.edu/~zli4/paper/PRMiner.pdf отлавливавшая ошибки в коде ядра Линукс на С++ может на порядки меньше. Хотя никакой тулзы не требует.
С++ не ловит все ошибки, он не может например ловить ошибки рантайма - это может только интерпретатор. Есть рабочий минимум контроля который должен делать компилер - например непонятки в семантике ( ++x=x++ ? --x :x--;  : -), есть ошибки которые он должен ловить для программеров, которые пишут прямо на экран, есть ошибки которые возникают при портировании.
Но доращивать его до полноценного ловщика ошибок - глупо, особенно если знать, что это в принципе невозможно. Разумнее написать отдельный полноценный ловщик ошибок, который ловит все. А компилятору оставить компиляторово.




> Нет, неправильно. Крупный проект ООП, это проэкт который использует длй решения задачи ооп подход, не только бизнесс логика, но и вся логика выражена через ООП.


 Здесь есть внутренне противоречие. Большинство крупных проектов ООП (на С++ , не джава) можно условно разделить на три компоненты - сеть, база и бизнес-логика. При этом первые две компоненты стандартны и только неумный человек будет их писать заново для каждого нового проекта. Говорю так, потому что сам имел глупость этим заниматься. 
Бизнес-логика уникальна для каждого проекта.
Баз объектных в природе не существует. Нет в реляционной модели понятия наследования. Также как и полиморфизма. Инкапсуляция возможна через крайне примитивный механизм грантов, но грантами для инкапсуляции пользоваться только мазохист.
Сетевая часть объектная есть - CORBA, еще один мертвец из OMG. Пользоваться им может тоже только мазохист - я например пользовался.  Большинство предпочитают хорошо зарекомендовавшие себя сокеты.
В результате получаем странную вещь. Две из трех компоненты, которые стабильны - никогда не пишутся в ООП-стиле. Хотя исходя из элементарной логики - это первое, что должно было бы быть переписано под ООП и разойтись по всему миру. Вам в этом ничего не кажется странным?

----------


## homo ludens

> это не обычное переполнение. у меня лонг 64 бит, и abs даже не поругался, что я ему даю вместо 32 бит 64. или число с плавающей точкой. а в с++, если вы забыли подключить заголовок, то ошибка, а если не забыли, то вызовется правильная функция. вот вам и система типов и перегруженные функции


 Вы С99 когда послений раз читали?
В С такая проверка не выполняется, а производится обычное преобразование типов. С доверяет программисту и считает, что программист знает что делает.
С++ не выловит всех таких ошибок, он выловит только то, что идет в функции с явным несовпадением типов, но он ничего не скажет, если вы напишете b = abs ( (short)b );. Иесли вы мне возразите - непишите такой код, то я вам отвечу - а вы не пишите такой как у вас. Ни один из методов не дает больше сохранности, просто один говорит программисту, что если ты будешь писать так - то это будет ошибка,  а другой метод говорит прямо противоположное. Размер множества потенциальных ошибок не меняется.
Это синтаксический сахар, не более того. 
А вот хороший синтаксический парсер + рантайм среда поддержки - выловит гарантированно все.




> не более, чем /usr/include/*


 Взял какой-то man в котором было написано что abs в algorithm. Очевидно старый или неправильный. Все мои С++ проекты сегодня находятся в поддержке, потому нормальный комплект доки на компе не держу.
С cstdlib действительно оверхед всего в полтора раза, что вполне допустимо и проблемой не является. 
Признаю свою ошибку в данном случае. Хотя подозреваю, что если мне придется юзать что-то темплейтное ситуация может измениться в мою сторону.




> специально для вас имеется в наличии std::string::append, все остальные могут использовать более интуитивный +


 


> C++'s biggest problem is its size. People will tell you that this is no big deal; just subset C++ down to whatever part of it you're willing to use; that can be a C subset or something a little larger, or the whole thing.
> 
> This logic can be used to justify an infinitely complex language, so one should hesitate to accept it at face value. 
> ...
> Another significant problem is that it requires an investment of effort to select an appropriate subset. Moreover, you will have difficulty finding books that teach this subset; and, indeed, if you acquire a C++ algorithms book, the odds that the subset chosen for that book matches that of yours is low.
> 
> In other words, subsetting is the same as fragmenting the language into lots of separate dialects; it has all the same problems as that, with the added cost of none of those dialects having unique names. *What does it mean to say "I've used C++ for six years" on a resume?*


 http://www.nothings.org/computer/cpp.html
выделено мной.




> в таком случае, с++, как язык с мощными средствами для создания библиотек, является ярким примером unix way.
> однако, непонятно, как более сложный синтаксис с++ делает его менее приспособленным для unix way. как раз наоборот, больше стимул сделать одну библиотеку вместо тысячи по своему кривых парсеров на коленке.
> вы можете взять другой парсер, или в конце концов сделать самую правильную библиотеку


 Проблема в том, что в С семантическая близость почти всегда кореллирует с синтаксической близостью. Это означает, что для разбора и понимания блока кода мне кроме этого блока кода практически ниего больше и не надо, кроме может быть прототипов. В С++ семантика размазана по всей программе. Проверка на malloc у меня происходит только там, где она нужна. catch - в любом месте программы. Это означает что любой синтаксический чекер должен читать всю программу у вас, а у меня - только нужный блок и h-файлы. Кроме того, программист - это такой же чекер и yo-yo problem - это его точка зрения на то, что я написал.
Кстати, большинство С-компиляторов не юзают грамматику С в формальном виде, парсер пишется ручками, без yacc/bison. Для С++ это невозможно.




> и что, в эти сокеты вставляются grep, sort и т.п. ?


 Даже это можно сделать, хотя имхо это мазохизм.  Тем не менее, два стандартных интерфейса, которые есть практически в любой базе - socket и shared memory. Второй правда больше для hi-end.




> я всего видел две базы данных. mysql написан на с++, postgresql сам себя называет object-relational dbms.


 mysql - обычная реляционка. Postgress - часть очень амбициозного проекта (рухнувшего) по приведению БД к ООП. Насколько я помню (могу ошибаться, а гуглить лень) автор Postgress был одним из авторов Второго манифеста, в котором заявлялось, что ООП порвет реляционки как тузик грелку. Результатом стало то, что он возглавил это направление в Информиксе. Я человек невезучий, я как раз писал тогда под Информикс. 
До его назначения Информикс входил в тройку ведущих производителей БД вместе с Oracle и Sybase. После - был продан IBM. В самом Postgress ООП реализован на зачаточном уровне и таким увы и останется. Называть его объектным нельзя, даже сам автор его так не называет, его любимое название - постреляционная БД.




> этот миф развенчан более 30 лет назад. основная сложность разработки по ложится на дизайн, а его сильно легче не сделать никаким языком и никакими генераторами кода. почитайте брукса


 Читал и "месяц" и "пулю". Кроме того как я уже и рассказывал, автоматическая генерация кода в отдельно взятой предметной области у меня уже реализована. И не только у меня.



> не правильный ответ. массив то у вас общий все равно, вот и ставьте ifdef вокруг его элементов


 именно это я и делаю. И для этого мне не надо иметь доступ к ни к одной функций инициализаторов, в т.ч и к общей. Encapsulation однако.
Опять таки мой вариант хорош, когда глобальный инициализатор пишется раз и навсегда, а инициализаторы подсистем - разными людьми. А решение о компиляции и запуске подсистемы принимает третий зам админа на деплойменте, куда я доступа не имею. И читать мой код будет чужой программист.
Имхо большиство флеймов между объектниками в инете - это разборки как писать правильно. 
Я так думаю, что если программа работает - значит она написана правильно. А если не работает - то нет. И все.



> т.е. msvc, sunpro и tru64cxx - это все компиляторы под linux или hurd  ?


 нет, это компиляторы под свои ОС. Кроме них есть еще компиляторы, под них тоже надо портировать...  В отличие от libc которая портируется под ОС а не под компилятор. Что я и хотел показать. Портировать библиотеку под компилятор, имхо противоречие в терминах. Тут или компилятор какой-то нестандартный или библиотека очень уж извращенная. 

Готов правда признать,  что похоже вы правы и живых портов glibc под другие ОС нет. Хотя Posix все равно никто не отменял и на тех системах h-файлы будут отличаться, но интерфейсы должны быть достаточно похожими, чтобы гарантировать портируемость.

----------


## homo ludens

> valgrind это отлично, но лучше не допускать ошибок, чем лихо их исправлять.


 тогда зачем вам проверка ошибок в С++?  




> ну, это просто не смешно. если у вас есть 1000 условий, приводящих к завершению программы


 причем здесь программа? Благодаря изоляции проверок в одном блоке я запускаю только этот блок. Это именно то, чего не получится при try-catch.
Запускаю, не я а скрипт, написанный раз и навсегда.
если у меня функция возвращает не то значение, то это проблема вызывающей функции, которая тестируется отдельно. Нет экспоненциального взрыва.
Концентрация кода против размазывания. Семантическая метрика должна быть максимально близка к синтаксической, иначе приходится вместо работы с участком кода работать со всей программой. Что и происходит в С++.

Более того, есть всего одна единственная вещь, которой здесь не хватает для того, чтобы гарантированно выловить *все* ошибки динамической памяти. Это хорошего синтаксического препроцессора. Когда-то у С++ был шанс таким стать, но он его упустил.

а 1000 условий в одной функции редко когда бывают...

С++ не сможет найти все ошибки по определению, а ужесточение правил компиляции ведет только нервности программистов пытающихся с помощью трюков побороть компилятор. Кстати им это обычно удается.

----------


## Ull9

> Блаженны люди, которые не видят собственных внутренних противоречий.
> Человек, который желает иметь компилятор, отлавливающий все ошибки и при этом не называется интерпретатором обречен на вечный когнитивный диссонанс. Как впрочем и человек, верящий в то, что здание ООП построенное на трех столбах (наследование, инкапсуляция, полиморфизм) будет устойчиво когда по крайней мере две из трех концепций противоречат друг другу.


 Не понял твоего креасноречия, но прошу тебя не надо ничего обьяснять. А то тебя унесет не туда. Это тема все таки программирование.



> Потому что кроме того, что он дает, он еще много забирает. Например возможности автоматической обработки исходного кода.
> Плюс еще много чего.


 А вот здесь, поконкретнее, что забирает с++ компилятор, от с компилятора?




> Я сам писал на HPUX, потому завидую хорошему железу и сочуствую плохому софту (блин на моей версии даже threads не было в природе).
> Когда ничего нельзя ставить на машину заказчика - у разработчика ставиться аналогичная машина с идентичным софтом и версией ОС вплоть до патчей (кстати, никому не нужна PA-RISC E-шка 10-летней давности?


 Я сижу у заказчика в его офисе, работаю у него на машине. ничего поставить дополнитель в его машинный зал нехочу да и не по карману мне.



> Тулзу можно сэмулировать malloc hooks либо просто подменой malloc.  Хотя это конечно гемор тот еще, плюс большие тормоза в отладке, но это работа одноразовая и на всю жизнь.
> Главное здесь возможность синтаксического анализа исходного кода и концентрация анализа ошибок  в том блоке, где они возникают.


 не позволит тебе такой тул поймать ошибку пока через него тред не пройдет.
Понял? А теперь представь, что тебе нужно проверить все if/then/switch/case ветки.
Вот здесь начнется твой геморр. Спасибо, не катит твой подход в реальном мире.



> С++ размазывает источник ошибки по всей программе, С- нет.


 поясни, я не понял, может мы говорим о разных вещах




> Опять таки, мой метод упрощен и примитивен. Даже такая софтина как http://opera.cs.uiuc.edu/~zli4/paper/PRMiner.pdf отлавливавшая ошибки в коде ядра Линукс на С++ может на порядки меньше. Хотя никакой тулзы не требует.
> С++ не ловит все ошибки, он не может например ловить ошибки рантайма - это может только интерпретатор.


 Ну положим и интерпретатор не отловит всеошибки тоже. существуют ошибки, которые дадут краш дамп. и не важно интерпретатор или компилятор. анализа меньше не будет.


Есть рабочий минимум контроля который должен делать компилер - например непонятки в семантике ( ++x=x++ ? --x :x--;  : -), есть ошибки которые он должен ловить для программеров, которые пишут прямо на экран, есть ошибки которые возникают при портировании.
Но доращивать его до полноценного ловщика ошибок - глупо, особенно если знать, что это в принципе невозможно. Разумнее написать отдельный полноценный ловщик ошибок, который ловит все. А компилятору оставить компиляторово.
[/QUOTE]
Нет разумнее не писать ловщик ошибок, а использовать компилятор на все 100%, и здесь с++ выгоднее с. причина та же, ловщик ошибок не покроет весь код при прогонах. во всяком случае это далеко не тривиальная задача. Так зачем это надо?



> Здесь есть внутренне противоречие. Большинство крупных проектов ООП (на С++ , не джава) можно условно разделить на три компоненты - сеть, база и бизнес-логика. При этом первые две компоненты стандартны и только неумный человек будет их писать заново для каждого нового проекта. Говорю так, потому что сам имел глупость этим заниматься. 
> Бизнес-логика уникальна для каждого проекта.
> Баз объектных в природе не существует. Нет в реляционной модели понятия наследования. Также как и полиморфизма. Инкапсуляция возможна через крайне примитивный механизм грантов, но грантами для инкапсуляции пользоваться только мазохист.
> Сетевая часть объектная есть - CORBA, еще один мертвец из OMG. Пользоваться им может тоже только мазохист - я например пользовался.  Большинство предпочитают хорошо зарекомендовавшие себя сокеты.
> В результате получаем странную вещь. Две из трех компоненты, которые стабильны - никогда не пишутся в ООП-стиле. Хотя исходя из элементарной логики - это первое, что должно было бы быть переписано под ООП и разойтись по всему миру. Вам в этом ничего не кажется странным?


 не пишутся, но очень часто обрамляются в ооп интерфейсы, да и сокеты тоже.
и вот после этого с ними работать очень просто.

----------


## homo ludens

> не позволит тебе такой тул поймать ошибку пока через него тред не пройдет.
> Понял? А теперь представь, что тебе нужно проверить все if/then/switch/case ветки.
> Вот здесь начнется твой геморр. Спасибо, не катит твой подход в реальном мире.


 так в том то все и дело, что с помощью второго шага (переопределения if) я могу гарантировать обход всех вариантов.
Я не могу так поймать case, не могу поймать ?: но все варианты прохождения программы по if' ам в этом блоке я выловлю.
См. пример.
В реальной жизни я юзаю не препроцессор а перл-скрипт для конверсии сырцов, там case ловится.




> поясни, я не понял, может мы говорим о разных вещах


 Прикол в том, что в С все источники глюков внутри блока могут быть выловлены внутри блока и для этого не надо никакой доп. информации.
А в С++ для вылавливания исключений или pvc или метода уже убитого класса мне нужна дополнительная информация из других модулей.
Причем если вместо синтаксического анализатора мы поставим негра-тестера, то его работа и его проблемы будут такими же как и у анализатора - проблема yo-yo.

Если у меня в С что-то находится рядом в момент выполнения то оно находится рядом и в коде. В результате выловить глюк в С просто, а в С++ - сложно.
Это я называю соответствием семантического и синтаксического расстояний. Вещи близкие по смыслу и логике находятся близко в коде и наоборот.




> Ну положим и интерпретатор не отловит всеошибки тоже. существуют ошибки, которые дадут краш дамп. и не важно интерпретатор или компилятор. анализа меньше не будет.


 Но он всегда выловит больше компилятора.
Вообще в теории креш интерпретатора вызвать можно, есть способы, но я пока ни разу не видел креш например перла. Это надо очень постараться.



> Нет разумнее не писать ловщик ошибок, а использовать компилятор на все 100%, и здесь с++ выгоднее с. причина та же, ловщик ошибок не покроет весь код при прогонах. во всяком случае это далеко не тривиальная задача. Так зачем это надо?


 Ловщик ошибок по определению справится с этим лучше компилятора. Более того, для разных вариантов использования можно брать разные ловщики, тестирование методом Монте-Карло или генетикой тоже вбивать в компилятор? Зачем?




> не пишутся, но очень часто обрамляются в ооп интерфейсы, да и сокеты тоже.
> и вот после этого с ними работать очень просто.


 В том то и дело, что они писались очень часто. Ни одна из реализаций не живая. Все дохлые. Написать оо-обвертку вокруг сокета можно, а триггеры БД тоже обвертками делать?. оо-вытаскиватель сырых данных из БД - это не оо-БД.
Если мы видим, что из трех компонент ООП- проекта только одна закрытая часть декларируется как ОО, то это наводит на подозрения. Это как выбирать из трех девушек-сестер, две из которых уродины а третья в парандже, но ее родственники говорят, что она красивая. А под паранджу заглянуть не разрешают.

----------


## Ull9

> так в том то все и дело, что с помощью второго шага (переопределения if) я могу гарантировать обход всех вариантов.
> Я не могу так поймать case, не могу поймать ?: но все варианты прохождения программы по if' ам в этом блоке я выловлю.
> См. пример.
> В реальной жизни я юзаю не препроцессор а перл-скрипт для конверсии сырцов, там case ловится.


 после того как ты переопределил if, все твоя програма начинает творить непонятно что, теперь тебе нужно переппределять return, нет ... не буду я заморачиватся, это геморр. Лучше писать сразу на ц++ и пользовать правильный дизайн. неубедил.



> Прикол в том, что в С все источники глюков внутри блока могут быть выловлены внутри блока и для этого не надо никакой доп. информации.


 прости, но это смешно, работаю я с чистым С тоже. в половине случеав наверное, источник глюка находился в другом файле вообще, а нередко вообще в другой либине. Но незнаю, как и где ты работаешь.. твои дела.




> А в С++ для вылавливания исключений или pvc или метода уже убитого класса мне нужна дополнительная информация из других модулей.
> Причем если вместо синтаксического анализатора мы поставим негра-тестера, то его работа и его проблемы будут такими же как и у анализатора - проблема yo-yo.


 что такое pvc?
что такое убитый класс?
нафик мне нужен синтаксический анализатор если у меня есть с++ компилер?
Конкретно, чем мне поможет синтаксически анализатор, и не поможет компилятор? Пример?



> Если у меня в С что-то находится рядом в момент выполнения то оно находится рядом и в коде. В результате выловить глюк в С просто, а в С++ - сложно.


 близко, неблизко. насколько я понимаю, мы говорим о стэке потока, в момент возникновения проблемы.
ПРИМЕР, дай пожалуйста, когда на одном и том же коде С и С++ дадут насолько разный стэк, что С стэк даст совсем близко проблему а С++ стэк оч далеко.




> Но он всегда выловит больше компилятора.


 слова.... ПРИМЕР, когда интерпретатор даст больше чем анализ креаш дампа. 



> Ловщик ошибок по определению справится с этим лучше компилятора. Более того, для разных вариантов использования можно брать разные ловщики, тестирование методом Монте-Карло или генетикой тоже вбивать в компилятор? Зачем?


 я непонимаю о чем м говорим, об ловле ошибок в момент компиляции, или о различных тест-стратегиях? но вроде как разговор о с и с++, нет?




> В том то и дело, что они писались очень часто. Ни одна из реализаций не живая. Все дохлые. Написать оо-обвертку вокруг сокета можно, а триггеры БД тоже обвертками делать?. оо-вытаскиватель сырых данных из БД - это не оо-БД.
> Если мы видим, что из трех компонент ООП- проекта только одна закрытая часть декларируется как ОО, то это наводит на подозрения. Это как выбирать из трех девушек-сестер, две из которых уродины а третья в парандже, но ее родственники говорят, что она красивая. А под паранджу заглянуть не разрешают.


 [/QUOTE]
не говори, прости глупостей, прекрасно все оболочки и вокруг оракла и вокруг сокетов работают. Твое выражение что они дохлые,,, это твое выражение.

Прости пустые слова. и про пример который ты не дал, и о похороненой КОРБЕ, и о мертвых ооп оболочках вокруг БД. 
И что в остатке, ты предлагаеш. самому писать какие то ловщики, причем сам же признаешь, что геморр.

И еще я не понял, повторю:
А вот здесь, поконкретнее, что забирает с++ компилятор, от с компилятора?

Примеры сэр, примеры, а то запишите сабя в пустозвоны

----------


## homo ludens

> после того как ты переопределил if, все твоя програма начинает творить непонятно что, теперь тебе нужно переппределять return, нет ... не буду я заморачиватся, это геморр. Лучше писать сразу на ц++ и пользовать правильный дизайн. неубедил.


 
Мне не надо больше переопределять ничего. if достаточно. Подумай еще немного и прогуляйся по коду.
Все твое непонятно-что будет в рамках выполнения if. Для контроля распределения и освобождения динамической памяти этого достаточно. Для других вещей (например выход за границы массива или вызов функции через нулевой указатель) нет. Тем не менее то, что контролирует мой вариант не контролирует (и никогда не будет этого делать) С++.
Если нужен детальный код макросов - могу дать.




> прости, но это смешно, работаю я с чистым С тоже. в половине случеав наверное, источник глюка находился в другом файле вообще, а нередко вообще в другой либине. Но незнаю, как и где ты работаешь.. твои дела.


 для отлова неосвобождения/непроверки на ноль динамической памяти в пределах блока мне гарантированно хватает информации в пределах блока. При условии соблюдения зоны ответственности за эту память. Это означает что для каждого куска динамической памяти всегда можно найти минимальный блок между скобками {} где эта память создается и удаляется, либо должна это делать. Информации между этими скобками достаточно для отлова глюка (на С). Для отлова глюка не требуется информация о всех вызываемых внутри блока функциях. Я могу точно получить точку в пределах блока где 
а. память удаляется повторно
б. точка где выделялась неосвобожденная память
в. точка где память не проверялась на 0.
Для этого мне не требуются сырцы никаких других блоков. Опять таки на С.




> что такое pvc?
> что такое убитый класс?
> нафик мне нужен синтаксический анализатор если у меня есть с++ компилер?
> Конкретно, чем мне поможет синтаксически анализатор, и не поможет компилятор? Пример?


 Pure virtual function call
индиректный вызов метода класса уже освобожденного деструктором.
эти вещи на уровне синтаксиса не ловятся, они вообще хз как ловятся. Нафик тебе надо - решай сам. Наверное у тебя компилятор находит все твои ошибки? Тогда откуда ты знаешь что такое креш-дамп?
Пример - сравни вывод splint с выводом С++.




> близко, неблизко. насколько я понимаю, мы говорим о стэке потока, в момент возникновения проблемы.
> ПРИМЕР, дай пожалуйста, когда на одном и том же коде С и С++ дадут насолько разный стэк, что С стэк даст совсем близко проблему а С++ стэк оч далеко.


 Я не говорю о стеке потока. Я говорю об исходном коде. Для локализации проблемы в исходниках С мне нужна лишь небольшая окрестность в области ошибки. В С++ мне нужен весь код.




> слова.... ПРИМЕР, когда интерпретатор даст больше чем анализ креаш дампа.


 memory leak. Особенно когда он накапливается в системе 24x7x365 - я с этим работал. У меня серваки мной написанные аптаймы по пять лет имеют. 
Плюс куча вещей когда структура памяти нарушается а креша нет. Плюс куча вариантов тихих race condition, когда все в порядке, только переменные путаются.




> я непонимаю о чем м говорим, об ловле ошибок в момент компиляции, или о различных тест-стратегиях? но вроде как разговор о с и с++, нет?


 я говорил о том, что идея ловить ошибки компилятором такая же глупость как компилировать дебаггером. В качестве примера привел ошибки динамической памяти которые не ловятся компилятором и никогда ловиться не будут, но другими способами ловятся без проблем. На С.




> не говори, прости глупостей, прекрасно все оболочки и вокруг оракла и вокруг сокетов работают. Твое выражение что они дохлые,,, это твое выражение.


 я говорил не про оболочки, а про объектные базы данных и объектно ориентированные сетевые интерфейсы. Напиши что нибудь на CORBA а потом сравни с оо- оболочками вокруг сокетов. ООБД промышленного класса сегодня нет. Не реализуется полиморфизм и наследование на реляционной модели. 




> Прости пустые слова. и про пример который ты не дал, и о похороненой КОРБЕ, и о мертвых ооп оболочках вокруг БД. 
> И что в остатке, ты предлагаеш. самому писать какие то ловщики, причем сам же признаешь, что геморр.


 Не нравится - не пиши. Я пишу себе инструментарий сам, ты - пользуешься чужим. У меня ошибок динамической памяти нет. Как у тебя - не знаю.




> И еще я не понял, повторю:
> А вот здесь, поконкретнее, что забирает с++ компилятор, от с компилятора?


 я уже сказал. Локальность кода, позволяющая быстро найти проблему. Простота синтаксиса, позволяющая писать синтаксические анализаторы и чекры. Отсутствие обратной совместимости. Низкое повторное использование кода. Единство языка - нет С++ программистов, есть С++ программисты собственного любимого диалекта-подмножества языка. Ты когда последний раз mutable в коде писал? Ограничения в типизации которые ничего не ограничивают, так как те, кто хочет ее перешагнуть с легкостью перешагивают, а остальных заставляет бороться с компилятором. Усложнение отладки и понимания логики чужого кода прошитого перегрузками. Мертвая привязка библиотек к языку. Общая агрессивность сторонников ООП в сочетании с маниакальным желанием переписать собственную версию всего, что уже давно есть, а если не переписать, то хоть обернуть в ОО-интерфейсы. 
C++ is the iMac of computing languages (с)





> Примеры сэр, примеры, а то запишите сабя в пустозвоны


 В начале дискуссии я предлагал найти общую систему ценностей и критерии, которые обе стороны приняли бы как объетивные. Это позволило бы избежать тупого флейма. Однако до сих пор от сторонников С++ я слышал только один аргумент - "мне так удобно" со ссылкой на личный опыт.

----------


## Ull9

я понял что наибольшие проблемы у тебя с....
>>>>
Для других вещей (например выход за границы массива или вызов функции через нулевой указатель) нет. Тем не менее то, что контролирует мой вариант не контролирует (и никогда не будет этого делать) С++.
Если нужен детальный код макросов - могу дать.
<<<<
но всех перечисленных тобою проблем в с++ при правильном дизайне просто нет.
еше раз проблемы с динамической памятью, выход за рамки массива, вызов нулевых указателей. это все решается на уровне кода/компиляции.
для С ты вынужден пиосать доморощеные макросы.

>>>>для отлова неосвобождения/непроверки на ноль динамической памяти в пределах блока мне гарантированно хватает информации в пределах блока. <<<<
Ну а если не в пределах блока. Что так редки у тебя сличай, когда в одном блоке даешь малок а в другом фри? Почему так сузил проблему?

>>>>Pure virtual function call
индиректный вызов метода класса уже освобожденного деструктором.
эти вещи на уровне синтаксиса не ловятся, они вообще хз как ловятся. Нафик тебе надо - решай сам. Наверное у тебя компилятор находит все твои ошибки? Тогда откуда ты знаешь что такое креш-дамп?
Пример - сравни вывод splint с выводом С++.
<<<<
ну а разве в С нет виртуальных вызовов? Ты когда нибудь поинтерами на функции пользовался? Это не есть ли прямой аналог виртуальной функции в с++?
Что такое класс освобожденный деструктором? Ты вообще знаесь с++?
В С++ нет возможности класс освобождать! НЕТ! кто тебе сказал такую глупость?
Ты вообще знаешь что такое класс, а что такое обьект? что можно делать с классами, что с обьектами?
Еще раз, при правильном дизайне у меня нет проблем с динамической памятью вообще.
вызовов методов уничтоженных ОБьЕКТОВ (НЕ КЛАССОВ) нет. То что с++ позволяет делаТъ ПРИ ПРАВИЛъНОМ ДИЗАЙНЕ, ты должен писать свои макросы.

ну и так далее по тексту...

----------


## pal

> Мне это сказал архитектор. из моего модуля недожны выпадать неописанные и непредусмотренные исключения. И я с ним согласен. вышестоящий модуль бросает свой эксепшн, некий А, но тот кто работает с моим модулем Б, понятия не имеет что работая с модулем Б, он кроме Б, должен ловить еще и А. причем искА и искБ, не имеют базового класа. И если Б выбрасывает усключение искА, все, краш.Что недопустимо, опять же архитектором.


 никто вам ничего подобного не говорил.  кто и как будет пользоваться нашим модулем, неизвестно. мой модуль не бросает вообще никаких Б, только то, что бросает его нижестоящий модуль.



> на то есть HLD, документ который описывает, что от твоего модуля требуется, что он может бросать, а что нет.


 и что, этот документ говорит заменять исключение кодом ошибки ? как вы возвращаете код ошибки из конструкторов ? ваша библиотека предусматривает использование в проектах, которых не было в планах на момент ее создания ? и вообще, где этот документ для нащего примера ?



> проблемы? подходяще действие, это вызов unexpected затем abort.


 ну, так если это подходящее действие, то и напрягаться не стОит, верно ?



> Ну будем считать что его уволили за плохой стиль, как ты выразился. хотя ты утверждаешь, что надо было уволить меня. Где же логика?


 логика очень проста. этот плохой стиль исправляется грепом, в отличие от не exception-safe программы



> Ну вот ты сам же и пишешь (пункт 1), что при твоем "правильном" подходе, как минимум один модуль, ДОЛЖЕН заниматься изврашениями


 ничего подобного. как мы уже выяснили, исключение можно вообще не ловить, если достаточно просто завершить выполнение. а если ловить надо, то в main его пробрасывать некуда. а не в main его можно пробрасывать в неизмененном виде. так что, извращениями заниматься никто не должен, я просто предложил вам это делать без меня 



> Теперь пункт 2. В постановке задачи, сказано что делать, возвращать 0. еще раз почитай пример на С. там все четко написано. Я так и зделал. ты - нет. ненадо никакой отсебятины.


 в постановке задачи сказано реализовать аналогичный интерфейс на с++. возвращать 0 никто не говорил, все началось со спора, что лучше возвращать - 0 или исключение. пример на с, естественно, может только вернуть 0.



> В нормальной жизни тебе говорит архитектор, что делать.


 как архитектор, я вам говорю: бросаться может любое исключение, если вам надо вызывать .what(), ловите std::exception, если надо просто освобождать ресурсы, ловите ... и пробрасывайте его дальше в неизменном виде.
ловить конкретный тип надо только, если вы знаете, что с ним делать. если в задаче ничего о конкретных типах не сказано, то и ловить их смысла нет.



> компания где я работаю (ну оч. известная), придерживается другой философии программирования.


 разве философия известных компаний позволяет ломать ресурсы при возникновении исключения ?

----------


## TechInsightJobs

гы

>для отлова неосвобождения/непроверки на ноль динамической памяти в пределах блока мне гарантированно хватает информации в пределах блока. При условии соблюдения зоны ответственности за эту память. 

особенно при любви к лонгджампу


>Общая агрессивность сторонников ООП в сочетании с маниакальным желанием переписать собственную версию всего, что уже давно есть, а если не переписать, то хоть обернуть в ОО-интерфейсы. 


особенно в свете "Не нравится - не пиши. Я пишу себе инструментарий сам, ты - пользуешься чужим."


> Однако до сих пор от сторонников С++ я слышал только один аргумент - "мне так удобно" со ссылкой на личный опыт.

Вы ссылаетесь на чужой?

----------


## Ull9

послушайте пал. мне спорить с вами не к чему. 
я, например, согласен с теми, которые говорят, что эксепшн вообще зря в стандарт ввели. но вернемся к топику

разговор, во всяком случае для меня, начался со следующего.
была нечеткая постановка задачи. был дан некий с код. Предлагалось его повторить.
это и есть небольшое техзадание от архитектора. какое есть такое есть.

И тут вы правы, что неизвестно кто и как будет вызывать этот код. ничего не сказано об этом. я даже добавлю. НИЧЕГО НЕ СКАЗАНО, ЧТО БУДЕТ С++ ЕГО ВЫЗЫВАТь.
этот код мог вызывать, скажем фортран код. мог? мог. фортран линкуется с С библиотеками? легко.

вот такое тех задание. повторить С код, но на С++. 

дале смотрим, итак некая финкция возвращает поинтер. ок? ок
и в случае нехватки памяти возврашается 0. 

а теперь посмотрим, что написали вы. что проиcxодит, если нехватит памяти?

А теперь вопрос. ВНИМАНИЕ.
является ли ваш код полностью эквивалентным С коду?
Не нарушили ли вы тех задание?

У вас два варианта ДА и НЕТ, Скажу сразу, если вы говорите НЕТ, я с вами закончу разговор. Если да, то мы продолжим о философии компании где я работаю.

----------


## pal

> Вы С99 когда послений раз читали?


 не думаю, что я его вообще читал



> В С такая проверка не выполняется, а производится обычное преобразование типов. С доверяет программисту и считает, что программист знает что делает.


 программист просто очепятался. доверяет программисту с++, а с ему при этом еще и не помогает.



> С++ не выловит всех таких ошибок, он выловит только то, что идет в функции с явным несовпадением типов, но он ничего не скажет, если вы напишете b = abs ( (short)b );.


 конечно. ведь с++ доверяет программисту и если программист применил явный каст, то значит так и задумывалось



> Иесли вы мне возразите - непишите такой код, то я вам отвечу - а вы не пишите такой как у вас.


 я возражу по другому - используйте static_cast, его грепить проще



> Ни один из методов не дает больше сохранности


 в приведенном примере с++ дал больше сохранности от глупой ошибки



> , просто один говорит программисту, что если ты будешь писать так - то это будет ошибка,  а другой метод говорит прямо противоположное. Размер множества потенциальных ошибок не меняется.


 как же не меняется, если с проглатывает все ошибки, а с++ только нетривиальные ?



> Это синтаксический сахар, не более того.


 теперь compile-time проверки так называются ?



> А вот хороший синтаксический парсер + рантайм среда поддержки - выловит гарантированно все.


 гарантированно все выловит только строгое доказательство, что выловлено гарантированно все



> http://www.nothings.org/computer/cpp.html
> выделено мной.


 мне нравится первый пункт "The Big Mistake: C Compatibility" 

мы же это уже обсуждали. с++ больше, чем с, но не намного. для решения более сложных проблем требуются более сложные инструменты, тем более, что пользоваться ими легче, а вся сложность все равно сосредоточена в прикладной области

всю ссылку я не осилил, т.к. автор просто болен



> Проблема в том, что в С семантическая близость почти всегда кореллирует с синтаксической близостью.


 особенно с макросами



> Это означает, что для разбора и понимания блока кода мне кроме этого блока кода практически ниего больше и не надо, кроме может быть прототипов. В С++ семантика размазана по всей программе. Проверка на malloc у меня происходит только там, где она нужна. catch - в любом месте программы.


 вы не поверите, но в с++ catch находится только там, где нужен и, в отличие, от кода ошибки, его нельзя забыть передать или проверить



> Это означает что любой синтаксический чекер должен читать всю программу у вас, а у меня - только нужный блок и h-файлы.


 это еще почему он должен читать всю ? делаете каждый отдельный кусок exception-safe и typesafe и все отлично работает. тогда как в с даже неизвестно, надо ли проверять возвращаемое значение функции х



> Кроме того, программист - это такой же чекер и yo-yo problem - это его точка зрения на то, что я написал.


 это не проблема с++. это даже не проблема ооп. это проблема либо дизайна, либо прикладной области.



> Кстати, большинство С-компиляторов не юзают грамматику С в формальном виде, парсер пишется ручками, без yacc/bison. Для С++ это невозможно.


 вообщето для с++ невозможно как раз обратное, т.к. его грамматика не lalr(1)
и какое это имеет значение ?



> Даже это можно сделать, хотя имхо это мазохизм.  Тем не менее, два стандартных интерфейса, которые есть практически в любой базе - socket и shared memory. Второй правда больше для hi-end.


 и как большие монолитные базы данных вообще относятся к unix-way ? или у вас "есть сокет==unixway" ? в с++ тоже есть сокеты



> mysql - обычная реляционка. Postgress - часть очень амбициозного проекта (рухнувшего) по приведению БД к ООП. Насколько я помню (могу ошибаться, а гуглить лень) автор Postgress был одним из авторов Второго манифеста, в котором заявлялось, что ООП порвет реляционки как тузик грелку. Результатом стало то, что он возглавил это направление в Информиксе. Я человек невезучий, я как раз писал тогда под Информикс. 
> До его назначения Информикс входил в тройку ведущих производителей БД вместе с Oracle и Sybase. После - был продан IBM. В самом Postgress ООП реализован на зачаточном уровне и таким увы и останется. Называть его объектным нельзя, даже сам автор его так не называет, его любимое название - постреляционная БД.


 не знаю, как его называет автор (разве у него есть один автор ?), но первая строчка описания пакета "PostgreSQL is an advanced Object-Relational database management system"
я все еще не понимаю, из чего мне делать выводы о недостатках с++



> Читал и "месяц" и "пулю". Кроме того как я уже и рассказывал, автоматическая генерация кода в отдельно взятой предметной области у меня уже реализована. И не только у меня.


 конечно, не только у вас. компилятор занимается автоматической генерацией кода, шаблоны занимаются автоматической генерацией кода, но генератору на вход все равно кто-то должен что-то подать. когда это можно будет делать неформально, отпадет необходимость не только в языках, но и в программистах.



> именно это я и делаю. И для этого мне не надо иметь доступ к ни к одной функций инициализаторов, в т.ч и к общей. Encapsulation однако.
> Опять таки мой вариант хорош, когда глобальный инициализатор пишется раз и навсегда, а инициализаторы подсистем - разными людьми. А решение о компиляции и запуске подсистемы принимает третий зам админа на деплойменте, куда я доступа не имею. И читать мой код будет чужой программист.


 вы попробуйте это написать, так чтобы компилятор понял, потому что по русски вы многое пропускаете. в моем варианте не нужно доступа больше, чем в вашем, и не надо считать, что написав тот же цикл вручную, вы что-то сэкономите



> Имхо большиство флеймов между объектниками в инете - это разборки как писать правильно. 
> Я так думаю, что если программа работает - значит она написана правильно. А если не работает - то нет. И все.


 в некоторых случаях достаточно и такого определения



> нет, это компиляторы под свои ОС. Кроме них есть еще компиляторы, под них тоже надо портировать...  В отличие от libc которая портируется под ОС а не под компилятор. Что я и хотел показать.


 вы показали что-то другое. glibc поддерживает _только_один_ компилятор. и как вы не выкручивайтесь, буст поддерживает его же и больше



> Портировать библиотеку под компилятор, имхо противоречие в терминах. Тут или компилятор какой-то нестандартный или библиотека очень уж извращенная.


 первое. почему для вас новость, что msvc - нестандартный компилятор ?



> Готов правда признать,  что похоже вы правы и живых портов glibc под другие ОС нет. Хотя Posix все равно никто не отменял и на тех системах h-файлы будут отличаться, но интерфейсы должны быть достаточно похожими, чтобы гарантировать портируемость.


 осталось признать, что буст представляет собой документацию интерфейса + reference implementation.

----------


## pal

> тогда зачем вам проверка ошибок в С++?


 затем, что проверка на стадии компиляции это как раз и есть "не допускать ошибок" ?



> причем здесь программа? Благодаря изоляции проверок в одном блоке я запускаю только этот блок. Это именно то, чего не получится при try-catch. .....


 все отлично получится. дальше поскипано, т.к. вообще непонятно, как можно рассуждать о том, может быть 1000 условий или нет ( как будто, если их только 100, то будет легче ), в то же время игнорируя примеры абсолютного маразма с изменением семантики и работоспособности только с операторами if



> С++ не сможет найти все ошибки по определению, а ужесточение правил компиляции ведет только нервности программистов пытающихся с помощью трюков побороть компилятор. Кстати им это обычно удается.


 конечно, он не может найти все ошибки, но избавление от самых распространенных ведет к успокоению нервов. да и трюками это называть не надо, хочется void* - вперед, никто не мешает, только потом не надо жаловаться.

----------


## pal

> я, например, согласен с теми, которые говорят, что эксепшн вообще зря в стандарт ввели.


 это вы зря



> разговор, во всяком случае для меня, начался со следующего.
> была нечеткая постановка задачи. был дан некий с код. Предлагалось его повторить.
> это и есть небольшое техзадание от архитектора. какое есть такое есть.
> .......
> вот такое тех задание. повторить С код, но на С++.


 не знаю, может в конце его можно было так понять, но изначально мы сравнивали методы реализации интерфейсов на с и с++. естественно, на с для с и на с++ для с++.



> А теперь вопрос. ВНИМАНИЕ.
> является ли ваш код полностью эквивалентным С коду?


 нет конечно, цель состояла в демонстрации преимуществ исключений перед кодами ошибок , как это можно сделать полностью эквивалентным кодом ?



> Не нарушили ли вы тех задание?


 нет



> Если да, то мы продолжим о философии компании где я работаю.


 спасибо, но пока не надо

----------


## Ull9

> спасибо, но пока не надо


 так зачем было ерничатъ и спрашивать? привести цитату?





> это вы зря
> не знаю, может в конце его можно было так понять, но изначально мы сравнивали методы реализации интерфейсов на с и с++. естественно, на с для с и на с++ для с++.


 Понять его сложно, но почитайте его конкретно пост, 
он просит написать код на с++. И здесь ваш код не эквивалентен



> нет конечно, цель состояла в демонстрации преимуществ исключений перед кодами ошибок , как это можно сделать полностью эквивалентным кодом ?


 Ну чтож, значит вы согласны, что код ваш НЕ СЛЕДУЕТ его коду, а значит вы закладывайте серьезную ошибку, если ставить ваш код вместо его кода. что и требовалось доказать.

Ну а теперь к другой теме.

чем же хороши эксепшны? вернее так появление эксепшнв в кодевносит серьезную неопределенность

например возьмем функцию
void* foo();

ески нет яксепшнв, то информации достаточно, вызываешь функцию, смотришь результат, если результат не 0, значит пользуйся этим поинтером, как тбе надо.
все однозначно.
Но в присутцрвии эксепшна все становится неопределенным, 
какой-либо эксепшн может быть выброшен? может да может нет.
какой эксепшн может быть, тип? совершенно не ясно?
в яве например, компилятор смотрит за соответствием типов эксепшна,
в с++ компилятор этого не делает.

Конечно можно договорится что бросать, что нет, но это согласитесь совсем другое. а именно с++ не гарантирует проверку типов.
остается что ?
catch (...)?
это уродливо.

Конечно можно писать 
void* foo() throw MyException,
НО, язык, не считает ошибкой, если этот throw отсутствует.
налицо неопределенность, это то мне и не нравится.

предвижу ваше возражение, что код возврата можно "забыть" проверить.
в отличие от эксепшна, его надо ловить, никуда не денешся.

Так вот наоборот, сушествует красивое решение Александреску. так, что код возврата невозможно не проверить, и компилятор об это заботится.
а вот эксепшн, прекрасно можно непоймать. и компилятор даже не предупредит.

----------


## pal

> так зачем было ерничатъ и спрашивать? привести цитату?


 вопрос был риторический 



> Ну чтож, значит вы согласны, что код ваш НЕ СЛЕДУЕТ его коду, а значит вы закладывайте серьезную ошибку, если ставить ваш код вместо его кода. что и требовалось доказать.


 дык, никто и не собирался ставить этот код вместо
спор сводился к вопросу "кому легче живется, программистам на с или на с++?"



> Ну а теперь к другой теме.
> 
> чем же хороши эксепшны? вернее так появление эксепшнв в кодевносит серьезную неопределенность
> 
> например возьмем функцию
> void* foo();
> 
> ески нет яксепшнв, то информации достаточно, вызываешь функцию, смотришь результат, если результат не 0, значит пользуйся этим поинтером, как тбе надо.
> все однозначно.
> ...


 неопределенность, конечно, есть. но совсем другого плана: может в этом промежутке произойти исключение или нет.
ответ на этот вопрос можно получить, посмотрев на прототипы вызываемых функций. если лень - считайте, что может. но вам почти никогда не надо знать тип исключения. например:
catch ( ... ) { delete p; throw; }
зачем вводить имя и тип переменной, которая вас не интересует? а еще лучше - использовать raii и тогда ловить вообще _ничего_ не надо и даже задумываться о наличии исключений не надо.
если принять, что для кода, которому не важно, какое именно исключение пришло, функция, бросающая любые исключения, ничем не отличается от функции, бросающей какие-то определенные, то практически все функции делятся на две группы - бросающие неизвестно, что и не бросающие ничего.



> Конечно можно писать 
> void* foo() throw MyException,
> НО, язык, не считает ошибкой, если этот throw отсутствует.


 спецификация типов исключений является не самым удачным свойством с++ и использовать ее рекомендуется пореже

----------


## pal

> Так вот наоборот, сушествует красивое решение Александреску. так, что код возврата невозможно не проверить, и компилятор об это заботится.
> а вот эксепшн, прекрасно можно непоймать. и компилятор даже не предупредит.


 проблема в том, что проверять его надо не в месте генерации, а там, где известно, что с ним делать. а туда его как-то надо передать. не говоря уже о том, что делать, если функция возвращает результат одного типа, а ошибки другого

----------


## Ull9

> неопределенность, конечно, есть. но совсем другого плана: может в этом промежутке произойти исключение или нет.
> ответ на этот вопрос можно получить, посмотрев на прототипы вызываемых функций. если лень - считайте, что может. но вам почти никогда не надо знать тип исключения. например:
> catch ( ... ) { delete p; throw; }


 Новы же сами, признавали, несколкими постами выше что это дурной стил.
catch (...)
Что поменялос?
я например, делая проверку кода других (частенко делат приходится), сразу отнечаю это как признак плохого дизайна! точно так же как и dynamic_cast, const_cast, void* и тд и тп. 




> зачем вводить имя и тип переменной, которая вас не интересует? а еще лучше - использовать raii и тогда ловить вообще _ничего_ не надо и даже задумываться о наличии исключений не надо.


 Да краеуголный камен с++ жесткая проверка типов. И почему мне от этого отказыватся?
а как же я пойму что за ошибка? что мне нехватает - памяти? или БД недоступно? или файловая система капут. И не надо мне говорит, что бросай эксепшн далше. может нет никого далше. может далше код фортран!



> если принять, что для кода, которому не важно, какое именно исключение пришло, функция, бросающая любые исключения, ничем не отличается от функции, бросающей какие-то определенные, то практически все функции делятся на две группы - бросающие неизвестно, что и не бросающие ничего.


 Вот это и слишком общее место, что значит бросающее что то? мне этого мало!
я должен принимат решение, что за эксепшн я получил, и вообще. понимат возможен ли эксепшн вообще теоретически из какойто функции, а у мнея этого нет!
Спрошу так тебе нравится работат с void*? мне нет, я считаю это злом, и признаком плохого дизайна. а ты предлагаеш по сути его.



> спецификация типов исключений является не самым удачным свойством с++ и использовать ее рекомендуется пореже


 Так я и говорю, что не толко спецификация, но и сама идея не реализована до конца.
так как например в ява, там компилятор НЕПОЗВОЛИТ, игнорироват эксепшн. И точно надо знат тип. в с++ пожалуйста, так можно делат.что ест дыра в безопасности

ПС. пардон мягкий знак пропал

----------


## pal

> Новы же сами, признавали, несколкими постами выше что это дурной стил.
> catch (...)
> Что поменялос?


 я явно признавал дурным стилем что-то другое.
catch (...){/*release*/; throw; } это самый часто применяемый вариант



> я например, делая проверку кода других (частенко делат приходится), сразу отнечаю это как признак плохого дизайна!


 быстрый grep '\bcatch\b' /usr/include/c++/4.1.1/*{,/*} возвращает _один_ catch (const bad_weak_ptr&) и полторы сотни catch(...)



> Да краеуголный камен с++ жесткая проверка типов. И почему мне от этого отказыватся?
> а как же я пойму что за ошибка? что мне нехватает - памяти? или БД недоступно? или файловая система капут.


 да какая же разница, если вам надо только освободить ресурс ? вы что, по другому будете его освобождать, если бд не доступна а не файловая система капут ? вот если вам надо как-то по умному реагировать, например выдать сообщение "не хватает памяти, закройте лишние окна", тогда надо знать. но это достаточно знать в одном месте на всю программу, а не после каждого вызова функции в каждой библиотеке.



> И не надо мне говорит, что бросай эксепшн далше. может нет никого далше. может далше код фортран!


 фортран не сможет вызвать функции с с++ линковкой



> Вот это и слишком общее место, что значит бросающее что то? мне этого мало!
> я должен принимат решение, что за эксепшн я получил, и вообще.


 еще раз, какое решение вы примете в примере с { delete p; throw; } ? по другому удалите ? или не удалите совсем ?



> понимат возможен ли эксепшн вообще теоретически из какойто функции, а у мнея этого нет!


 ответ содержится в прототипе. как я уже говорил, если лень смотреть в прототип, считайте, что может



> Спрошу так тебе нравится работат с void*? мне нет, я считаю это злом, и признаком плохого дизайна. а ты предлагаеш по сути его.


 тут есть важное различие. указатель не хранит свой тип, а исключение хранит, даже если мы в этом обработчике его не спрашиваем.



> Так я и говорю, что не толко спецификация, но и сама идея не реализована до конца.
> так как например в ява, там компилятор НЕПОЗВОЛИТ, игнорироват эксепшн. И точно надо знат тип. в с++ пожалуйста, так можно делат.что ест дыра в безопасности


 можете привести пример, демонстрирующий преимущество явы ?

----------


## Ull9

> я явно признавал дурным стилем что-то другое.
> catch (...){/*release*/; throw; } это самый часто применяемый вариант


 обьясните мне, в чем разница
catch ( ... ) { delete p; throw; }
catch (...){/*release*/; throw; }
первое вы называете дурным стилем, а второе хорошим стилем?




> да какая же разница, если вам надо только освободить ресурс ? вы что, по другому будете его освобождать, если бд не доступна а не файловая система капут ? вот если вам надо как-то по умному реагировать, например выдать сообщение "не хватает памяти, закройте лишние окна", тогда надо знать. но это достаточно знать в одном месте на всю программу, а не после каждого вызова функции в каждой библиотеке.


 да в том то и дело, если недоступна база я обязан закрыть свои сокеты и стучатся в нее (БД) пока она не откроется, а если не могу прочесть файл конфигурации, то мне нужно вежливо закрыть приложение, не только в ресурсах дело.



> еще раз, какое решение вы примете в примере с { delete p; throw; } ? по другому удалите ? или не удалите совсем ?


  По обстоятельствам, мне нужно знать обстоятельства! может и не буду. мне черно-белого флажка недостаточно. а вообще я бы смарт поинтеры для ресурсов применял, дались они вам. не только в ресурсах дело. может например мне без этого ресурса можно продолжеть работу, а может и нет.



> ответ содержится в прототипе. как я уже говорил, если лень смотреть в прототип, считайте, что может


 Так, в том и дело, что мне недостаточно знать что может, мне нужно знать тип.
это как функхия возвращает void*. какой ит нее толк? и с++, здесь терят свою строгость и однозначность. позволяет думать и предпологать что угодно. это мне и не нравится.



> можете привести пример, демонстрирующий преимущество явы ?


 пожалуйста, если функция в яве бросает эксепшн, то компилятор проследит. чтоб кто нибудь выше этот яксепшн ловил, иначе ошибка компиляции.
с++ это непроверяет, вообще никак, а разница между прочим огроманя между поиманым и непойманым эксепшном. это уже рантаим наступил, и это краш программы.

----------


## homo ludens

> особенно в свете "Не нравится - не пиши. Я пишу себе инструментарий сам, ты - пользуешься чужим."


 мой инструментарий расширяет функциональность и пишется раз и навсегда. ОО-оболочки обвертывают существующий код и функциональность не расширяют. Сапожник, который ходит в сапогах чужих производителей и не умеет поставить на них заплатку - не сапожник.




> Вы ссылаетесь на чужой?


 Прочитаейте еще раз историю флейма. Там все есть, на что я ссылаюсь - на публикации в инете, на здравый смысл, и самое главное - на логические противоречия в концепци. Возможно там есть и мой личный опыт, но имхо я его не привлекаю так часто как мои оппоненты.




> но всех перечисленных тобою проблем в с++ при правильном дизайне просто нет.


 Назови мне программу, которая получая на вход исходный код говорит - правильный у нее дизайн или нет. Если такой программы нет - то твоя команда программистов зависит от мнения старших товарищей. Которе резко меняется при смене
1. товарищей
2. фирмы
3. команды.
4. идеологии
и т.п.
Если нет формального критерия "правильного дизайна" то все о чем ты говоришь - спекуляции на всеобщей убежденности, что такой критерий есть. А его нет.




> для С ты вынужден пиосать доморощеные макросы.


 извини, но мой доморощеный макрос уже в состоянии решить больше проблем чем С++ компилятор когда нибудь сможет. Это я не к тому, что мой макрос так хорош, скорее С++ такой какой есть.




> Ну а если не в пределах блока. Что так редки у тебя сличай, когда в одном блоке даешь малок а в другом фри? Почему так сузил проблему?


 Подумай еще раз и почитай мой пост внимательней. Если в одном блоке есть выделение памяти а в другом освобождение, то где-то в коде обязательно найдется блок, вызывающий оба. В худшем случае это будет main() {};
В этом блоке кода будет найдена ошибка, точнее номер строки кода внутри блока где эта ошибка возникает. Скорее всего там будет вызов другой функции, ее блок тоже можно проверить. В С нет связности кода по определению, простым последовательным пересмотром всех блоков кода можно все найти.
Для С++ это неверно, например глобальные объекты, определенные до main вообще не имеют вызывающих блоков, а отследить граф вызова с перегрузками, конструктароми временных объектов и т.п. вообще нельзя.




> ну а разве в С нет виртуальных вызовов? Ты когда нибудь поинтерами на функции пользовался? Это не есть ли прямой аналог виртуальной функции в с++?


 нет, не аналог. В С есть индиректный вызов функции и программисты им пользуются, но только тогда, когда это действительно надо. В большистве случаев для С это другой вариант конструкций switch/case, не более того. Динамический объект который содержит динамический адрес функции - вещь намного более редкая, чем в С++.
В С++ это обычная практика потому проблемы этого вида происходят намного чаще.
варианты когда в С возможна ошибка - это два основных варианта. Прямой вызов указателя на функцию при нулевом или неопределенном значении указателя. Ловится за 5 минут, большинство случаев даже без рантайма.
Вызов "метода" - есть указатель на структуру, членом которого есть указатель на функцию. Родительский указатель 0 или неопределен. Ловится так же.




> Ты вообще знаешь что такое класс, а что такое обьект? что можно делать с классами, что с обьектами?


 извини, опечатался. Ты рад, что нашел у меня ошибку? Спроси меня еще, что такое метакласс, я правда знаю. 
втречный вопрос - а ты знаешь что можно делать с классами с помощью метаобъектного протокола? 



> То что с++ позволяет делаТъ ПРИ ПРАВИЛъНОМ ДИЗАЙНЕ, ты должен писать свои макросы.


 В С++ не формального алгоритма отличия правильного дизайна от неправильного. Это порождает кучу хлебных мест для людей, которые решают что правильно, а что - нет.
Макросы свои я не должен писать. Я их уже написал и забыл когда это сделал. Это разовая работа, которая дала мне преимущество перед правильным кодом С++ - вы раз за разом вынуждены писать "правильно" убеждая себя в том, что это не в напряг. Мне уже не надо ничего писать.




> ну и так далее по тексту...


 так все-таки что разумнее - требовать от компилятора невозможного, компенсируя его недостатки неформальными критериями "правильности" прораммы или пойти по Unix way - компилятор компилит, стайл чекер проверяет стили, ран-тайм чекер - проверяет выполнение, а статический чекер - исходный код?

----------


## homo ludens

> особенно с макросами


 как ни странно - даже с ними.
Тем более что если они не нравятся - легко можно работать с выходом gcc -E.



> вы не поверите, но в с++ catch находится только там, где нужен и, в отличие, от кода ошибки, его нельзя забыть передать или проверить


 или не находится. пример неопределенности - ваш спор с Ull9.




> это еще почему он должен читать всю ? делаете каждый отдельный кусок exception-safe и typesafe и все отлично работает. тогда как в с даже неизвестно, надо ли проверять возвращаемое значение функции х


 ключевое слово - делаете. А если не сделали - сами виноваты. А если не сделал другой - виноват он.
А проверить автоматом нельзя. А если нельзя автоматом - то и у человека, проверяющего будут проблемы - он такой же автомат, только сложнее.
В С любая случайно сгененрированная программа, которая компилируется, может быть разбита на блоки, анализируемые отдельно. В с++ это не так. А индийский программист не всегда отличается генератора случайных программ.




> это не проблема с++. это даже не проблема ооп. это проблема либо дизайна, либо прикладной области.


 если бы это было так, то эта проблема наблюдалась бы и в других языках. Это неправда.




> вы попробуйте это написать, так чтобы компилятор понял, потому что по русски вы многое пропускаете. в моем варианте не нужно доступа больше, чем в вашем, и не надо считать, что написав тот же цикл вручную, вы что-то сэкономите


 мой цикл имеет то, чего нет у вас - стабильность кода. Функция, которая прогоняет этот цикл никогда и никем не правится. Программист, который придет после меня вставит свою подсистему в одном единственном файле - с определением массива. Если требуется раздельный вариант инициализации для однопоточной и многопоточной системы - у меня это сделать просто, у вас - нельзя.
Мой вариант гибче вашего и повышает стабильность кода.




> вы показали что-то другое. glibc поддерживает _только_один_ компилятор. и как вы не выкручивайтесь, буст поддерживает его же и больше


 Мне кажется, мы просто не синхронизировали терминологию.
Когда мы говорим "портировано под платформу" - мы понимаем разные вещи.
1. хардвер
2. Ос
3. Компилер.
Сравнивая по первому пункту, получаем что boost отдыхает.
Сравнивая по второму отдыхает glibc.
Сравнивая по третьему получаем тоже - glibc компилится только gcc.
Последние два пункта вполне понятны, во первых glibc является составной частью ОС линукс, а воторых поддержка ansi С здесь выгод практически не дает - одни минусы, учитывая высокий уровень портируемости самого gcc.
Интерфейсы однако, которые мы обсуждали, являются частью не glibc а POSIX+ISO C. Они по прежнему имеют более высокую портируемость, чем boost - причем по всем трем параметрам.




> гарантированно все выловит только строгое доказательство, что выловлено гарантированно все


 Вы valgrind-ом когда послений раз пользовались?

----------


## Ull9

> мой инструментарий расширяет функциональность и пишется раз и навсегда. ОО-оболочки обвертывают существующий код и функциональность не расширяют.


 Раширят, еще как раширяет, с помошэ враппера, я могу переименовать функцию, добавить новую, или убрать существующую. легко



> Назови мне программу, которая получая на вход исходный код говорит - правильный у нее дизайн или нет. Если такой программы нет - то твоя команда программистов зависит от мнения старших товарищей. Которе резко меняется при смене
> 1. товарищей
> 2. фирмы
> 3. команды.
> 4. идеологии
> и т.п.
> Если нет формального критерия "правильного дизайна" то все о чем ты говоришь - спекуляции на всеобщей убежденности, что такой критерий есть. А его нет.


 Как ты ловко подменил тему спора. Я не утверждал что с++ должен распозновать плохой дизайн. Я не утверждал, что само наличие с++ компилятора освобждает программиста писатъ правильный дизайн. А правильный дизайн, яето вешь довольно обьективная, и даже мой спор с Пал. если нам дать четкие условия на задачу, мы породим приблизительно один дизайн. 



> извини, но мой доморощеный макрос уже в состоянии решить больше проблем чем С++ компилятор когда нибудь сможет. Это я не к тому, что мой макрос так хорош, скорее С++ такой какой есть.


 например, что. конкретно? с++ & правильный дисайн против твоих макросов. Давай конкретный пример, когда твой макрос справится с тем, с чем с++ не справится.
У меня естьпару примеров когда твой макрос не справится, но я подожду для начала твои.



> Подумай еще раз и почитай мой пост внимательней. Если в одном блоке есть выделение памяти а в другом освобождение, то где-то в коде обязательно найдется блок, вызывающий оба. В худшем случае это будет main() {};


 ну а если ты пишешь библиотеку? если ты не видишь main() если у тебя многопоточное приложение, если ты даже не знаешь с уверенностью где этот блок? 



> можно проверить. В С нет связности кода по определению, простым последовательным пересмотром всех блоков кода можно все найти.


 ты не сможешь простым пересмотром определить этого. есть динамическое создание обьектов, есть их уничтожение, все в динамике. анализ кода без выполнания этого не выловит.



> Для С++ это неверно, например глобальные объекты, определенные до main вообще не имеют вызывающих блоков


 Хмм. а в С значит глобальных переменных нет? глобальные обьекты кстати, вешь нехорошая, и в правольном дизайне их быть недолжно, или сведено к самому минимуму. А С без них не может. наличие глобальных обьектов это наследие от С. вот ты и вынужден писать свои макросы. но у с++, при правильном дизайне их просто нет. 



> а отследить граф вызова с перегрузками, конструктароми временных объектов и т.п. вообще нельзя.


 так и не надо его следить. Вот в чем фишка, С++ с правильным дизайном все сам красиво отследит и память не утечет.



> нет, не аналог. В С есть индиректный вызов функции и программисты им пользуются, но только тогда, когда это действительно надо. В большистве случаев для С это другой вариант конструкций switch/case, не более того. Динамический объект который содержит динамический адрес функции - вещь намного более редкая, чем в С++.


 Редкая, но означет ли что ее нет вообще? и что делает твой макрос если она таки есть?
Все? перестает работать?




> В С++ это обычная практика потому проблемы этого вида происходят намного чаще.


 при правильном дизайне, (смарт-поинтер) никогда.



> варианты когда в С возможна ошибка - это два основных варианта. Прямой вызов указателя на функцию при нулевом или неопределенном значении указателя. Ловится за 5 минут, большинство случаев даже без рантайма.


 без рантайма, без тестов, которые покроют все возможные варианты входных данных это отследить невозможно. На то она и динамика.



> извини, опечатался. Ты рад, что нашел у меня ошибку? Спроси меня еще, что такое метакласс, я правда знаю. 
> втречный вопрос - а ты знаешь что можно делать с классами с помощью метаобъектного протокола?


 Я нашел у тебя грубую ошибку. Так обычно говорят те, кто слабо знают ООП.
радости тут мало, скорее мне жаль.



> В С++ не формального алгоритма отличия правильного дизайна от неправильного.


 я никгда этого неговорил, ты споришь сам с собой.



> Макросы свои я не должен писать. Я их уже написал и забыл когда это сделал. Это разовая работа, которая дала мне преимущество перед правильным кодом С++ - вы раз за разом вынуждены писать "правильно" убеждая себя в том, что это не в напряг. Мне уже не надо ничего писать.


 Еше раз пример! давай пример, когда твои макросы круче с++. Пример! я устал от слов.



> так все-таки что разумнее - требовать от компилятора невозможного, компенсируя его недостатки неформальными критериями "правильности" прораммы или пойти по Unix way - компилятор компилит, стайл чекер проверяет стили, ран-тайм чекер - проверяет выполнение, а статический чекер - исходный код?


 компилятор комилит, согласен
стайл чекер проверятет стили? отчасти комилятор проверит стиль. окончательно стильможет проверить только человек. А что ты называешь стилем?
ран-тайм чекер? это тесты? ну так что должны быть тесты? никто не спорил.
статический чекер? не спавится, 
давай примеры, голословный ты наш.

----------


## homo ludens

> Раширят, еще как раширяет, с помошэ враппера, я могу переименовать функцию, добавить новую, или убрать существующую.


 wrapper на IP сокет не добавит тебе функциональность TCP. И, кстати, использование таких оболочек в большинстве своем означает процедурное мышление под ОО-маской.



> Как ты ловко подменил тему спора. Я не утверждал что с++ должен распозновать плохой дизайн. Я не утверждал, что само наличие с++ компилятора освобждает программиста писатъ правильный дизайн. А правильный дизайн, яето вешь довольно обьективная, я невстречал двух разных мнений. и даже мой спор с Пал. если нам дать четкие условия на задачу, мы породим приблизительно один дизайн.


 Если условием функционирования С-программы является ее правильное исполнение на уровне юнит-теста во всех возможных ветвлениях, а для С++ - тоже самое + хороший дизайн, то С препочтительней. Потому что убирает субъекивную составляющую. Для тебя объективность - одинаковость решения. Для меня - наличие формального алгоритма. Ощути разницу.
Соответствие С-блока условиям задачи может быть полностью установлено формально. С++ -нет.




> например, что. конкретно? с++ & правильный дисайн против твоих макросов. Давай конкретный пример, когда твой макрос справится с тем, с чем с++ не справится.
> У меня естьпару примеров когда твой макрос не справится, но я подожду для начала твои.


 Рантайм (в моем случае valgrind) проверят ошибки рантайма. Компилятор рантайм не проверяет.
Макрос + рантайм проверят ошибки рантайма в тех ветвлениях, которые при нормальной работе программы достижимы редко. 
Примеры, когда макрос не справится у меня тоже есть. Опять-таки, синтаксический препроцессор эти проблемы решит. Я могу подменять макросами if и while но не могу подменить for. Однако С++ не решит ни одну проблему, которую решают макрос+рантайм.



> ну а если ты пишешь библиотеку? если ты не видишь main() если у тебя многопоточное приложение, если ты даже не знаешь с уверенностью где этот блок?


 Библиотека состоит из функций, каждая из которых есть блок. Для качественной библиотеки пишутся юнит тесты, где есть main и вызов всех методов библиотеки со всеми use case. Я во всяком случае в своих либах так делаю.
Для многопоточных приложений race condition так не отлавливаются, но это и не ставится в задачу. Хотя были техники, основанные на статическом анализе кода, но там много ложных срабатываний. Race conditions требует действительно полного анализа кода, а не только блока. Там я работаю чуть по другому - в зависимости от требований приложения. Здесь универсальных рецептов пока нет.
В valgrind входит тулза для отлова race conditions однако ей пользоваться тяжело - с ее точки зрения надо лочить мютексы на каждую функцию, в т.ч. на те, которые атомарны по определению (например printf - атомарен).





> ну а сли строк много? скажем 500 тыс? не проще ли компилятор заставить правильно работать?


 O(n) для С, намного больше для С++. Компилятор не установит соответствия между блоками компилирующимися отдельно. Плюс еще много все. Поймите. Компилятор не может по определению отловить все ошибки. Следовательно пытаться его в этом направлении улучшать - бесполезное дело. Статический чекинг+рантйм чекинг сделают это лучше и в теории способны на абсолютное решение основных проблем.




> Хмм. а в С значит глобальных переменных нет? глобальные обьекты кстати, вешь нехорошая, и в правольном дизайне их быть недолжно, или сведено к самому минимуму. А С без них не может. наличие глобальных обьектов это наследие от С.


 Нет конструкторов и деструкторов глобальных переменных.
А тот, кто тебе сказал, что С без них не может - соврал.
Есть устаревшие библиотечные функции, которые привязаны к глобальным переменным. В большинстве своем есть их непортируемые реентрантные аналоги.




> так и не надо его следить. Вот в чем фишка, С++ с правильным дизайном все сам красиво отследит и память не утечет.


 Нереально. Плюс правильный дизайн без формальных спецификаций правильного дизайна. 
Полный анализ такой фигни - это задаче больше чем O(n). А если компилятор работает не в O(n) - то это я просто не знаю как называется. Забудьте про ночные билды. Как минимум.
Статический чекинг не связан этими ограничениями. 

Блин, я никак не пойму, если все так хорошо, откуда ты знаешь тогда слова креш-дамп?




> Редкая, но означет ли что ее нет вообще? и что делает твой макрос если она таки есть?
> Все? перестает работать?


 ничего. Это у меня не ловится и задачи этогой не стояло. Я писал под свой стиль и для себя. В динамических объектах я обычно пользую не указатель на функцию, а индекс в соответствующей таблице.
Опять таки, задачей макроса не ставилось найти все глюки - без синтаксического препроцессора это нереально.
Кстати для моей комбинации это реального значения не имеет.
valgrind поймает разадресацию инвалидного указателя. А макрос обеспечит чтобы при тестах прошли те ветвления, где это может произойти. Так что скорее всего на реальный отлов глюков это не повлияет.

прогоняя чужой код С++ через valgrind вижу два постоянных глюка - вызовы виртуальных методов дестроенных объектов и pvc.  Наверное программисты хреновые, в дизайн не въезжают.




> при правильном дизайне, (смарт-поинтер) никогда.


 Это то, что я называю скрытым интерпретатором. смарт-пойнтер при использовании без мозгов в большинстве случаев реализует в рантайме то, что можно отследить в статике. Переходи на жаву, тебе понравится.

Ближайший аналог смарт-пойтеров в С - хендлы, пример которых можно увидеть в pthread.h




> Я нашел у тебя грубую ошибку. Так обычно говорят те, кто слабо знают ООП.
> радости тут мало, скорее мне жаль.


 brainbench'ами по ООП и С++ померяемся? Мои кажется еще не истекли.
Как знаток ООП - расскажи нам что такое метаобъектные протоколы и компиляторы  на С++. Более функциональным аналогом которых являются синтаксические парсеры, за которые я говорю.
Не знаешь принципов ООП ты, если называешь объектными реализациями враппер над сокетами. Под CORBA писал? Напиши и сравни.




> я никгда этого неговорил, ты споришь сам с собой.


 я не спорю, а утверждаю. Без формальной спецификации все разговоры о правильности дизайна - туфта.
Формальной спецификации нет (и кстати не может быть).




> Еше раз пример! давай пример, когда твои макросы круче с++. Пример! я устал от слов.


 круче С++ - без проблем. Круче С++ плюс правильный дизайн - увы нет. Потому что нам надо будет понять что такое правильный дизайн.
Мои макросы + рантайм не могут быть хуже компилятора, так как компилятор не отслеживает рантайм вообще. Если бы он отслеживал, то он назывался бы по другому.



> компилятор комилит, согласен
> стайл чекер проверятет стили? отчасти комилятор проверит стиль. окончательно стильможет проверить только человек. А что ты называешь стилем?


 выход astyle например. Проверяется элементарно. Или проверка идентации - настолько интеллектуальная техника?



> ран-тайм чекер? это тесты? ну так что должны быть тесты? никто не спорил.


 если у тебя есть друзья с линуксом, попроси их запустить за тебя valgrind. Пусть они тебе расскажут. Тесты, блин...



> Нет разумнее не писать ловщик ошибок, а использовать компилятор на все 100%, и здесь с++ выгоднее с. причина та же, ловщик ошибок не покроет весь код при прогонах. во всяком случае это далеко не тривиальная задача. Так зачем это надо?


 Твои слова?
Мне трудно спорить с человеком, который говорит, что ему ненужно штуки баксов, потому что купить яхту все равно не хватит. С логикой что-то у тебя не то.




> статический чекер? не спавится,


 сравни выход splint с выходом С++. второй раз говорю, не позорься.
Повторяю - все нормальные компиляторы работают в O(n). Статическому чекеру эти неувязочки пофиг. Ты не в академии "Шаг" учился? Это имхо на младших курсах проходят.




> давай примеры, голословный ты наш.


 мне кажется тебе хочется поругаться?
примеры ты видел, но по прежнему утверждаешь:
1. Никому не нужна система отлова ошибок - С++ + правильный дизайн делают все необходимые проверки.
2. Никому не нужна система отлова ошибок - она все равно не может поймать все, поэтому надо пользоваться С++, так как С++ + правильный дизайн делают все необходимые проверки.
3. Никому не нужна связность кода, С++ + правильный дизайн решают все вопросы.
4. Статический чекер не справится, потому он не нужен, С++ + правильный дизайн делают все необходимые проверки.
5. Рантайм чекер не пройдет всех веток, потому он не нужен. С++ + правильный дизайн делают все необходимые проверки.

Подтверди это или опровергни в следующем посте. 
Или я буду думать, что говорю с ботом.
Цитаты из твоих сентенций могу привести. Если забыл.

----------


## Ull9

> wrapper на IP сокет не добавит тебе функциональность TCP.


 я же сказал легко. например я ввожу хартбиты. и если соединение оборвалось. я сразу вижу что ТСП оборвался. Или ввожу авторизацию и аутонтетикацию, да мало ли что э делал с сокетами.
И так далее по всему тексту на все твои высказывания у меня есть ответы устал тебе возражать и устал по клаве стучать
ПРИМЕРЫ сэр, ПРИМЕРЫ когда будут?
Вот на примерах и продолжим.

----------


## homo ludens

примеры были. Ты их не увидел. Последний мой вопрос игнорировал. Значит я говорю с ботом.
Спорить с ботом, который считает, что компилятор может заменить рантайм-чекинг - бессмысленно.

----------


## Ull9

я никогда не говорил, что компилятор заненит тесты, никогда
я говорил что компилятор и правильный дизайн никогда не дадут некоторых ошибок
в принципе, например твой любимой потери памяти. это чуть ли не красной нитью у тебя.

Ну а стобой действительно говорить не очем кроме слов ни одного примера.
да и трепешся ты неряшливо. кроме твоего "удаленного класса" у тебя полно других ляпов. устал я от твоей словесной

пс, мне уже и в личку пишут, чтоб с тобой не связывался.
форум не я один читаю, и поверь, сразу видно кто трепло.

----------


## Guffy

в спор Ц и Ц++ влазить не буду
лень, да не люблю я Ц. надеюсь, товарищи в силах сами отбиться, да и буде уже 3е на 1го 

а вот по поводу ексепшинов вставлю 5 копеек

я во делаю так 

пусть есть Ц++ функция 


```
std::string foo()
```

 
 и надо ее вызывать из Ц, фортрана и хз знает еще чего
пишеться обертка flat-API


```
extern C {
 int flat_foo(char* buffer, int capacity){
   if(!buffer)
    return -1;
   try {
      std::string res=foo();
      if(capacity<=res.size())
        return-2;
      strcpy(buffer, res.c_str());
   }
   catch(std::bad_alloc ex)
   {
     return -3;
   }
   catch(...)
   {
     return -4;
   }
   return 0;
 }
}
```

 т.е. пока вы остаетесь в пределах Ц++ - смело руководствуйтесь рекомендациями *pal* - ловить конкретные екзепшины там где знаете что с ними делать. а ловить для освобождения ресурсов - так я предпочитал смарт-обертки с деструкторами.
когда же вам надо из Ц++ выходить - там и танцуйте с бубном.


ЗЫ: за правильность названий типа std::bad_alloc и т.п. не ручаюсь, по памяти, на Ц++ плотно сидел, да уж больше года на ЦШарп

----------


## lexar

спор нелепый
машина в конце-концов все на ассемблере выполняет
и на нем можно выразить абсолютно точно, что где и как нужно делать
но цена?

бред это все.
на С в большом проекте можно наделать больше ошибок
только по причине процедурно-ориентированной концепции
и разработка будет тянуться дольше

все течет развивается и меняется
На C#, к примеру, проекты при аналогичной
функциональности продвигаются быстрее чем на С++

А в хвосте прогресса остаются ниши для каждого из языков.
Более узкие, специализированные.

----------


## lexar

А кому не нравятся эксепшены
возвращайте коды возврата и выставляйте глобальные переменные.
Как в виндах: взял GetLastError и читай себе, что за хреновена у тебя не получилась.

Но это от лукавого.

----------


## homo ludens

имхо глобальные переменные - абсолютное зло, так как при переводе под мультипоточность вынуждают полностью переписывать код.
Можно потом выкрутиться привязкой глобальных переменных к потоку, но это мазохизм. Лучше сразу писать реентрантный код.

На С# проекты обязательно будут продвигаться быстрее.  Когда-то скорость разработки на дельфи и VB уверенно обгоняла С и С++. 
Имхо PHP здесь может быть еще лучше. 
Просто есть ниша RAD, в которой ни С++ ни С делать нечего по определению - у них другое предназначение.
А спор (пока он не превратился в холивор) я пытался перевести на фундаментальные недостатки дизайна С++ и самое главное - причины их появления (например design by committee - термин Струдструпа). Увы не получилось.
Каждый новый язык кажется лучшим, но потом приходит следующее поколение и споры начинаются опять. Мне кажется, что интересно было бы представить следующее поколение языков и инструментария, то которое еще не появилось и не рекламируется в журналах и не раскручивается производителями. 
С моей точки зрения в этом новом мире С++ уйдет и уйдет не как Pascal (сохранив свою нишу), а как COBOL. Т.е. практически полностью. Возможно что я не прав, предсказывать вообще дурацкое занятие, но некоторые основания для таких выводов у меня есть.

PS
In IEEE Computer, February 95, Prof. Wirth labeled C++ as "a language that discourages structured thinking and disciplined programming construction".
(c) http://www.eptacom.net/pubblicazioni/pub_eng/stroustr.html
давно это было...

----------


## THRESHE

> С моей точки зрения в этом новом мире С++ уйдет и уйдет не как Pascal (сохранив свою нишу), а как COBOL. Т.е. практически полностью. Возможно что я не прав, предсказывать вообще дурацкое занятие, но некоторые основания для таких выводов у меня есть.


 скорее дурацкое предсказание 

и какая ниша у Паскаля интересно ?

----------


## homo ludens

Паскаль и дельфи долгое время рулил на рынке RAD (Rapid application development) и никакой С и С++ их оттуда вышибить не мог. Время разработки на Дельфи было меньше, чем на С и С++, так же как время разработки на TurboPascal было меньше чем на TurboC во времена 8-дюймовых дисковводов.  
Во многом это было обусловлено простым синтаксисом и скоростью компиляции (опять таки из-за простого синтаксиса).
Сейчас Дельфи вытесняеся новыми языками, возможно Сшарп его убьет окончательно, однако количество одесских программеров-дельфинов по прежнему велико и есть фирмы, работающие только на Дельфи.
Так что ниша осталась до сих пор и еще будет какое-то время.
Другой вопрос - куда денутся потом все эти VCL библиотеки написанные годами дельфистами - в канализацию, вслед за tpu?
После фортрана и С остались огромные блоки кода, которые можно юзать в любом языке, после лиспа - алгоритмы с той же универсальностью, а после Дельфи? 
И, кстати, что останется от С++ в аналогичной ситуации?

----------


## THRESHE

> Паскаль и дельфи долгое время рулил на рынке RAD (Rapid application development) и никакой С и С++ их оттуда вышибить не мог. Время разработки на Дельфи было меньше, чем на С и С++, так же как время разработки на TurboPascal было меньше чем на TurboC во времена 8-дюймовых дисковводов.  
> Во многом это было обусловлено простым синтаксисом и скоростью компиляции (опять таки из-за простого синтаксиса).
> Сейчас Дельфи вытесняеся новыми языками, возможно Сшарп его убьет окончательно, однако количество одесских программеров-дельфинов по прежнему велико и есть фирмы, работающие только на Дельфи.
> Так что ниша осталась до сих пор и еще будет какое-то время.
> Другой вопрос - куда денутся потом все эти VCL библиотеки написанные годами дельфистами - в канализацию, вслед за tpu?
> После фортрана и С остались огромные блоки кода, которые можно юзать в любом языке, после лиспа - алгоритмы с той же универсальностью, а после Дельфи? 
> И, кстати, что останется от С++ в аналогичной ситуации?


 Я думаю вы намного лучше знаете что С++ это не RAD язык. Так что тут и сравнивать нечего. Понятно что на С# и Java писать можно быстрее, но вот проекты на С++ все равно намного качественней и быстрей (но соответственно и дороже). Так что для серьезных приложений думаю у С++ пока нет конкурентов. И по всей видимости в ближайшее время не будет

----------


## lexar

> После фортрана и С остались огромные блоки кода, которые можно юзать в любом языке, после лиспа - алгоритмы с той же универсальностью, а после Дельфи? 
> И, кстати, что останется от С++ в аналогичной ситуации?


 Куча ActiveX-ов, которые можно юзать даже из HTML  :smileflag:

----------


## homo ludens

Проекты на С++ бывают качественней, бывают быстрее, тут к сожалению много зависит и от архитекторов. Однако в скорости разработки они все-таки проигрывают VM-языкам. Очевидно, что на С++ нет смысла писать вещи, которые должны быть быстро проданы, а есть смысл писать что-то, что требует эффективности. Однако здесь он начинает конкурировать с С плюс к тому, что многие техники С++ не поощряют эффективность.

По поводу постоянно звучащего высказывания о том, что все современные крупные проекты пишутся на С++.
Прикол в том, что два крупных проекта выполненных на С++ обычно на самом деле используют разные языки, между собой плохо совместимые.
Например в стайлинг гайде одного крупного проекта в пункте с 4-х значным номером вы можете найти категорический запрет использования exceptions в любых ситуациях.
А в другом проекте - что-то совершенно другое, например запрет множественного наследования, использования mutable и обязательные виртуальные деструкторы в соответствующих классах.
Наверное все это С++, но один ли это язык на самом деле? 
camel is a horse designed by committee (с)
А лишние горбы (explicit или mutable) каждый обтачивает напильником сам.

2 lexar
OCX после VB оставалось куча. И сколько людей сегодня использует тот VB-код?

----------


## THRESHE

> С++ — язык, который изучается постепенно. Лишь после того, как будет сделан последний шаг,
> разрозненные приемы и фрагменты синтаксиса начинают складываться в общую картину. По-моему,
> изучение С++ чем-то напоминает подъем на лифте.
> 
> Дзынь! Второй этаж. С++ — это
> усовершенствованный вариант С, с сильной типизацией (которую, впрочем, при желании можно
> обойти) и удобными комментариями //. Любой программист на С, если он не хочет подаваться в
> менеджеры, должен двигаться дальше… а Бьярн Страуструп (Господи, благослови его) придумал для
> этого отличную возможность.
> ...

----------


## homo ludens

Неплохие аналогии, со многим согласен.
Сам долго катался на этом лифте и когда-то был большим фанатом этого языка.
Просто есть этажи выше, куда С++ увы уже не довезет. 
А мировоззрение (как и наркотик) имхо надо регулярно менять.

----------


## THRESHE

> Просто есть этажи выше, куда С++ увы уже не довезет.


 ИМХО С++ довезет везде (прям внедорожник  :smileflag:  )

Если можно примеры куда не довезет С++

----------


## SWARM

> ИМХО С++ довезет везде (прям внедорожник  )
> 
> Если можно примеры куда не довезет С++


 Любая экспертная система ))
Сложные транзакции на несколько баз данных )) (особенно когда нет С++ интерфейса для БД) )))

----------


## homo ludens

добавление свойств рефлексивности в язык (т.е. возможность перестройки синтаксиса на лету путем прямого доступа синтаксического препроцессора к ядру компилятора) дает намного больше в плане создания собственных языков.
Темплейты по сравнению с этим кажутся убогими и вызывают жалость.
Теоретически это возможно и в С++, даже было сделано в проекте OpenC++, однако пользоваться этой штукой в С++ крайне затруднительно.
Примерно как писать шелл-скрипт на С++ интерпретаторе 
В какой-то момент С++ мог пойти по этому пути, но теперь поздно.

----------


## THRESHE

> добавление свойств рефлексивности в язык (т.е. возможность перестройки синтаксиса на лету путем прямого доступа синтаксического препроцессора к ядру компилятора) дает намного больше в плане создания собственных языков.


 И смысл в этом ? Не хватает синтаксиса С++  :smileflag:  ?
Тем более что на С++ это можно сделать

----------


## Guffy

> Любая экспертная система ))
> Сложные транзакции на несколько баз данных )) (особенно когда нет С++ интерфейса для БД) )))


 Вы меня извините, но по моему это в все в пользу бедных
Экспертная система в первую очередь зависит от выбранного "экспертного алгоритма", а реализовать ее можно хоть на том же Ц (для мазохистов). А Ц++ тут как раз не самый последний (хотя, возможно и не первый). Вообще "экспертная система" - понятие растяжимое. Я лично был знаком с комрадами, которые лет десять назад писали экспертную систему на Ц++ - и ниче, не жужжали.
"Сложные транзакции" -  это вообще в афоризмы.

----------


## Guffy

> добавление свойств рефлексивности в язык (т.е. возможность перестройки синтаксиса на лету путем прямого доступа синтаксического препроцессора к ядру компилятора) дает намного больше в плане создания собственных языков.


 по моему, Вы тут путаете мух с котлетами. 
насколько я понимаю рефлексивность, так это возможность из кода исследовать *тип* объекта. Посмотреть его поля, методы, свойства, аттрибуты (т.е. так как это в дотнете делается), вызывать методы. Это, вероятно, было бы в Ц++, полезно. Особенно для задач сериализации. А то обычный RTTI довольно бедный (т.е. практически вообще "никакой")

если Вы имеете ввиду генерацию кода на лету в виде низкоуровневых команд (как в дотнете Reflaction.Emit), то у ГНУ был подобный проект. Название, извините, запамятовал. 

Разбор кода (его текста) в синтаксическое дерево, модификация дерева и обратно генерация текста (в дотнете CodeDom) - тоже не проблема. Погуглите, к примеру, проект SUIF. Но к рефлексии это не относится.

Компиляция куска кода на лету - вообще не вопрос. только зачем?

----------


## Ull9

совершенно верно Гуффи,
на с++ можно писать все что угодно, и экспертные системы, и рефлективность,
вопрос только зачем? Можно и на с писать. ну и что?

существуют для каждой задачи свой набор инструментов, и иногда надо писать на с иногда на с++, а иногда и на каком нибудь АспектJ.

Нет универсального языка, и это хорошо.

а чаще всего, проишодит так, приходишь на фирму. а там уже весь фреймворк на яве, вот и пишешь дальше на ней, и никакой пролемы выбора.

----------


## Guffy

"Эт точно" (с) Сухов

----------


## homo ludens

рефлексивность изанчально в исследовательских работах понималась как возможность языка модифицировать собственную грамматику (extensible compilers/interpreters). К генерации кода на лету (JIT-компиляторы) это не имеет никакого отношения. Когда в массы пришло ООП, этот термин стал более узким - он понимается как метаобъектные протоколы, дающие доступ к структуре класса (а не объекта). Реально это намного меньше, чем extensible compiler, позволяющий не только разбирать собственные исходные тексты, но и модифицировать грамматику.
SUIF действительно может похожие вещи, хотя его цели определены несколько расплывчато (An Infrastructure for Research on Parallelizing and Optimizing Compilers??), как и многие аналогичные проекты он выглядит чисто исследовательским проектом.

----------


## Guffy

изначально и автомобиль подразумевался не как роскошь, а как средство передвижения
и рефлексия в широкой жизни - это сейчас что имеем, то имеем. а не то   что подразумевалось в работах великих фантастов

----------


## homo ludens

кстати точка зрения, что каждой задаче - свой язык, достаточно разумна и я ее всячески поддерживаю.
Однако есть ряд подводных камней.
Допустим такая задача (реалистичная).
Нужно сделать экспертную систему (ЭС).
Дерево решений должно быть оптимизировано по алгоритму построения минимального дерева решений c4.5
Даные на вход - куча текста.
много математики и статистики, работа с матрицами.
процедуры работы с онтологией должны быть получены генетическим программированием (ГП). 

Что имеем.
ЭС - пролог.
с4.5 - стандартная реализация на С.
куча текста - перл.
математика - matlab/octave.
ГП - лисп.

каждый из этапов пишется легко. Практически каждый из предметных языков не типизирован и позволяет быстро ваять прототипы и модели, на которых просто отладить алгоритмы и найти удовлетворительные параметры.
А вот как связать все это вместе?
Кроме того заказчик хочет видеть у себя самодостаточную прикладуху с гуем, а не набор рекомендаций по установке пролога, перла и лиспа. 

Я в таких случаях пишу каждый кусок на прикладном языке, а вот потом, когда в модели все отстроено и работает, ручками перевожу в один низкоуровневый язык. С++ или С. Можно автоматом, но опыт показывает, что ручками лучше и не намного дольше, плюс прибавляет понимания как это на самом деле работает.
Что интересно, если не касаться ГУИ, то использование С++ в данной технологии в качестве конечного языка не дает практически никакого выигрыша по сравнению с С - все структуры и отношения к этому моменту уже расписаны и логика программы и каждой компоненты абсолютно прозрачна. Если я знаю например параметры хеша, то мне не нужен С++ map, мне лучше взять более низкоуровневую реализацию с прогнозируемыми параметрами.
С ГУИ однако ситуация меняется, увы. Имхо если бы производители ГУИ работали бы так же как и производители BLAS-реализаций, мир был бы другим.

----------


## lexar

> Что интересно, если не касаться ГУИ, то использование С++ в данной технологии в качестве конечного языка не дает практически никакого выигрыша по сравнению с С - все структуры и отношения к этому моменту уже расписаны и логика программы и каждой компоненты абсолютно прозрачна.


 А работаем в одиночку или бандой в 40 человек?
ИМХО, если бандой,
то уже одно использование управления видимостью
(шлепни по рукам соседа private-ом!)
дает колоссальное приемущество.

Замечание в документации: "не трогай эту глобальную переменную:
она моя и временная", -
приятель может не заметить,
а ошибку компиляции проигнорировать не возможно!

----------


## homo ludens

больше 10 человек банда никогда не собиралась. Имхо это не такие задачи которые решаются грубой силой и шеренгами негров-кодеров в кубиклах. За время, которое пройдет в попытках объяснить негру почему все-таки map не надо использовать можно его кусок кода самому написать.

А для С изоляция может быть (и должна быть) более строгой, чем для С++.
private виден в hh-файле, а typedef void* mytype не дает никакой информации о содержимом кроме интерфейса. Плюс отсутствие перекомпиляции при добавлении приватных методов/членов и соответственно более прозрачное разбиение на компоненты.
На С++ это можно сделать только используя чистые интерфейсы, а не классы, что имхо более геморройно и провоцирует на множественное наследование интерфейсов. А это тоже не всегда хорошо.
Плюс замечание в документации - не трогать exceptions. 
Хотя везде есть свои недостатки. Я не утверждаю, что С идеален, но в данном варианте разработки (а он тоже имеет проблемы, например большЕе время создания продукта) мне С++ никаких преимуществ не дает.
Возможно в проектах типа системы управления авиалайнером, где заняты тысячи человек это не работает.

----------


## SWARM

> Вы меня извините, но по моему это в все в пользу бедных
> Экспертная система в первую очередь зависит от выбранного "экспертного алгоритма", а реализовать ее можно хоть на том же Ц (для мазохистов). А Ц++ тут как раз не самый последний (хотя, возможно и не первый). Вообще "экспертная система" - понятие растяжимое. Я лично был знаком с комрадами, которые лет десять назад писали экспертную систему на Ц++ - и ниче, не жужжали.
> "Сложные транзакции" -  это вообще в афоризмы.


 И чем закончилось ? Написали и она до сих пор работает ? ))

----------


## Guffy

вроде не жаловались

----------


## Ull9

> А для С изоляция может быть (и должна быть) более строгой, чем для С++.
> private виден в hh-файле, а typedef void* mytype не дает никакой информации о содержимом кроме интерфейса.


 поинтер void*, порождает большие проблемы, особенно в больших проектах.



> Плюс отсутствие перекомпиляции при добавлении приватных методов/членов и соответственно более прозрачное разбиение на компоненты.


 Паттерн pimpl, пректрасно решает "отсутствие перекомпиляции при добавлении приватных членпв/методов".



> На С++ это можно сделать только используя чистые интерфейсы, а не классы, что имхо более геморройно и провоцирует на множественное наследование интерфейсов. А это тоже не всегда хорошо.


 Не более гоморойно, чем выражать виртуальность в чистом С.



> Плюс замечание в документации - не трогать exceptions. 
> Хотя везде есть свои недостатки. Я не утверждаю, что С идеален, но в данном варианте разработки (а он тоже имеет проблемы, например большЕе время создания продукта) мне С++ никаких преимуществ не дает.


 Давай конкретно, возьми любй пример применения виртуальности, и перепиши его на С. а потом подсчитай количество строк в примере и у себя.

----------


## homo ludens

Специально для Ull9 - ответы на вопросы

1. виртуальные функции
декларация


```
//код на С++
class foo
{
  virtual int bar(int par);
};

//код на С
struct foo
{
   int  (*bar)(struct* this,int par);
};
```

 странно, одинаковое количество строк.

имплементация


```
//C++
int foo::bar(int par)
{
 ...
}
//C
int bar_instance(struct* this,int par)
{
 ...
}
```

 тоже самое.

вызов


```
//C++
foo_inst.bar(4);
//C
(*foo_inst.bar)(&foo_inst,4);
```

 тоже самое.
синтаксический сахар, короче.

единственная где возникает оверхед по строкам (одна строка), это в динамическом инициализаторе/конструкторе (если он есть). В С я должен явно написать foo_inst->bar=bar_inst; Все.
Если же инициализация статическая, то и там оверхеда нет.

Зато свежеобученный или натасканный собеседованиями С++-ник начинает лепить наследование везде, где только можно. С виртуальными членами и абстрактными классами (причем в половине случаев забывая виртуальные деструкторы со всеми вытекающими последствиями :- ).
А на С - использование виртуалок - редкая практика, в полном соответствии с заветами отцов ООП - использовать композицию вместо наследования везде, где это возможно.

2. Private implementation
Паттерном эта штука называется редко, обычно используется термин "идиома". На кой черт потребовалось давать этому примитиву специальную аббревиатуру - вопрос к имиджмейкерам маркетинговых отделов соответствующих фирм.
В *правильном дизайне* (хе-хе) он нафиг не нужен, так как эта проблема должна решаться интерфейсами. И только.
Имхо PIMPL - заплатка на плохом дизайне.
Что характерно, в С обычно используется именно "интерфейсный подход", там такой штуки как PIMPL я еще не встречал, да она и не нужна.
Т.е. использование С- кода вида 


```
typedef void* opaque_foo;

opaque_foo opaque_foo_new(void);
int opaque_foo_delete(opaque_foo);
int opaque_foo_dosomething(opaque_foo);
```

 эквивалентно С++ коду.


```
// ничего, что я дефайны юзаю?
#define interface struct;

interface opaque_foo
{
  opaque_foo(void)
  virtual ~opaque_foo(void)=0;
  virtual dosomething(void)=0;
}
```

----------


## Ull9

Prezhde vsego proshu proshhenija za translit, tut na AIX naprjazhenka s russkimi bukvami..

Nee. tak delo nepojdet. u ob'ekta dolzhno byt' sostojanie. i togda povtori kod na c!

primer:

class Isound
{
   public:
   virtual void sound() =0;
   virtual ~Isound(){}
}

class dog:public Isound
{
   const char s;
   void sound () {printf(s);}
   public:
   dog::dog():s("Gav"){}
}

class cat:public Isound
{
   const char s;
   public:
   dog::dog():s("Mjau"){}
   void sound () {printf(s);}
}

int main()
{
   Isound *f = new dog;
   f->sound();
   delete f;
   f = new cat;
   f->sound();
   delete f;
}
posle jetogo podschitaj stroki. na C.

odin vopros ostalsja neotvechennym, void* est' zlo. sporit' budem?

pimpl idioma ili pattern sporit ne budu, plohoj dizajn ili net, tozhe. inogda naprimer on vynuzhdennyj. kak na AIX.

i poslednee, kod kotoryj priveden toboj C i C++, mozhet jekvivalentny mozhet i net. vse reshaet chto za obekty za c++ interfejsom.

----------


## homo ludens

ответ разобью на 2 части.

итак твой пример. Я чуть подправил, чтобы компилилось без ошибок.


```
#include <stdio.h>

class Isound
{
  public:
  virtual void sound() =0;
  virtual ~Isound(){}
};

class dog:public Isound
{
  const char *s;
  void sound () {printf(s);}
  public:
  dog::dog():s("Gav\n"){}
};

class cat:public Isound
{
  const char *s;
  public:
  cat::cat():s("Mjau\n"){}
  void sound () {printf(s);}
};

int main()
{
  Isound *f = new dog;
  f->sound();
  delete f;
  f = new cat;
  f->sound();
  delete f;
}
```

 34 строки
имхо здесь свалены в кучу две вещи - использование интерфейсов и виртуалки.

Сначала виртуалки.
На С виртуалки в данном примере не нужны вообще - есть внутреннее состояние, которое полностью определяет поведение объекта. Без скрытия внутренних структур код такой:


```
#include <stdio.h>
#include <stdlib.h>

typedef struct 
{
  const char *snd;
} sound_t;

void sound_print(sound_t* s)
{
  printf("%s\n",s->snd);
}

sound_t* new_dog(void)
{
  sound_t* rv=malloc(sizeof(sound_t));
  rv->snd="Gav!";
  return rv;
}

sound_t* new_cat(void)
{
  sound_t* rv=malloc(sizeof(sound_t));
  rv->snd="Mjay!";
  return rv;
}

int main(void)
{
  sound_t* f=new_dog();
  sound_print(f);
  free(f);
  f=new_cat();
  sound_print(f);
  free(f);  
  return 0;
}
```

 37 строк - на С аж на 3 строки больше.
Согласись, что если бы ты писал в нормальном стиле, не описывая члены класса в определении класса, на С получилось бы меньше.

теперь о интерфейсах.
Если реализовать скрытие через интерфейс, то в твоем примере интерфейсный класс Isound был бы вынесен в отдельный hh-файл.
На С это выглядело бы так.


```
// snd.h - интерфейсы
typedef void* sound_t;

void sound_print(sound_t* s);
sound_t* new_dog(void);
sound_t* new_cat(void);

// snd_int.h - внутренние структуры, скрытые от пользователя
#include <stdio.h>
#include <stdlib.h>

#include "snd.h"

typedef struct 
{
 const char *snd;
} sound_int_t;

void sound_print(sound_t* s)
{
  printf("%s\n",((sound_int_t*)s)->snd);
}

sound_t* new_dog(void)
{
  sound_int_t* rv=malloc(sizeof(sound_int_t));
  rv->snd="Gav!";
  return (sound_t*)rv;
}

sound_t* new_cat(void)
{
  sound_int_t* rv=malloc(sizeof(sound_int_t));
  rv->snd="Mjay!";
  return (sound_t*)rv;
}

// пользовательский код, доступа к внутренним структурам не имеет.
#include <stdlib.h>

#include "snd.h"

int main(void)
{
  sound_t* f=new_dog();
  sound_print(f);
  free(f);
  f=new_cat();
  sound_print(f);
  free(f);  
  return 0;
}
```

 в этом варианте получается строк больше, однако у тебя тоже по файлам не разбито.
имхо надо более реалистичный пример.
например у меня предполагается отсутствие деструктора, память освобождается через free, это не всегда верно для сложных структур. С другой стороны у тебя тоже деструкторы не используются. И т.п.

----------


## homo ludens

По поводу pimpl. Имхо как заплатка на уже существующий проект он неплох, но сам факт его использования означает, что в какой-то момент интерфейсы "поплыли" и надо использовать заплатки. Если бы они были зафиксированы, то pimpl не требуется. Я в таких случаях стараюсь все-таки сделать рефакторинг системы, хотя это конечно не всегда возможно.

void* является абсолютным злом для тех, для кого строгая типизация - абсолютное благо. Я к таким не отношусь. Есть вещи когда это надо, есть когда строго противопоказано. В небольших проектах, в скриптовых языках, в языках исследовательских и для быстрой разработки отсутствие контроля типов является жизненно необходимым. Введение контроля типов в perl или lisp просто убило бы эти языки. С другой стороны в многокомпонентных проектах с очень развитыми структурами данных без строгой типизации никуда.
С обеспечивает достаточно строгую типизацию за единственным исключением - void*. Я не считаю это оптимальным решением, однако (имхо) void* дает слишком много возможностей, чтобы от него отказываться и очень удобен на практике. В моем примере нет надобности писать sound_int_t* rv=(sound_int_t*)malloc(sizeof(sound_int_t)), однако  явное преобразование в return (sound_t*)rv необходимо.
Запрет преобразования void* в стандарте С++ с моей точки зрения был вполне разумен - С++ не С и в нем есть другие средства. Единственная проблема -  нарушение обратной совместимости с С, т.к. имхо такие вещи делать просто 
нельзя. 

Кстати, встречный вопрос, есть ли возможности реализовать в С++ полиморфизм без использования наследования? Мне это действительно интересно.

----------


## Ull9

> ответ разобью на 2 части.
> 
> итак твой пример. Я чуть подправил, чтобы компилилось без ошибок.
> 
> в этом варианте получается строк больше, однако у тебя тоже по файлам не разбито.
> имхо надо более реалистичный пример.


 конечно пример не реалистичен, и ты этим воспользовался.

ты воспользовался тем что у меня реализация интерфейсов содержит одну единственную команду print (s); и на основе этого построил свой контрпример.
Но твой контрпример развалитскя если я поменяю поведение имплементации кардинально.
допустим, так
dog::sound()
{
   Beep(1000, 1000); //1000 Hz, 1000 ms
}

----------


## Ull9

> По поводу pimpl. Имхо как заплатка на уже существующий проект он неплох, но сам факт его использования означает, что в какой-то момент интерфейсы "поплыли" и надо использовать заплатки. Если бы они были зафиксированы, то pimpl не требуется. Я в таких случаях стараюсь все-таки сделать рефакторинг системы, хотя это конечно не всегда возможно..


 как раз мой случай. большой проект рефакторинг исключен. выход? применяем пимпл.
это как раз меньшее зло. взамен  получаем отсутсвие компиляционных зависимостей.
И я так понимаю что ты признаешь свою неправоту вот здесь



> . Плюс отсутствие перекомпиляции при добавлении приватных методов/членов и соответственно более прозрачное разбиение на компоненты.


 с++ с этим справляется без пойнтера воид.

далее, относительно пойнтера воид, совершенно верно строгая типизация необходима, особенно в больших проектах. эти грабли уже проходили. лучше перебздеть чем недобздеть. а воид* дырка в типизации, и сам видел, как куча времени и бюджета в эту дырку утекало.

----------


## Ull9

> Кстати, встречный вопрос, есть ли возможности реализовать в С++ полиморфизм без использования наследования? Мне это действительно интересно.


 http://en.wikipedia.org/wiki/Standard_Template_Library

----------


## homo ludens

> конечно пример не реалистичен, и ты этим воспользовался.


 Вообще-то ты сам дал такой пример, я сам удивился, нафига здесь виртуалки.



> И я так понимаю что ты признаешь свою неправоту вот здесь
> 
> ...Плюс отсутствие перекомпиляции при добавлении приватных методов/членов и соответственно более прозрачное разбиение на компоненты....


 С чего бы это?
ты хочешь сказать, что в моем варианте нужна перекомпиляция? Или у меня менее прозрачное разбиение на компоненты, чем pimpl?
перечитай мой пост - я писал, что на С++ это надо делать интерфейсами.
А если надо применять pimpl - значит надо найти того, из-за кого его надо применять и дать по голове, чтобы четче интерфейсы проектировал.




> а воид* дырка в типизации, и сам видел, как куча времени и бюджета в эту дырку утекало.


 Чего я еще не встречал, так это таких примеров. Забыть виртуальный деструктор - это завсегда, а вот проблем с void* - это извернуться надо.
Проблемы с типами (не void*) чаще всего возникают когда программист пытается бороться с ограничениями языка и придумывает такое, что язык не выносит.
Как твоя типизация спасет от такого - http://bash.org.ru/quote.php?num=66390

примеры


```
//C++
#include <stdio.h>

class Isound
{
  public:
  virtual void sound() =0;
  virtual ~Isound(){}
};

class dog:public Isound
{
  const char *s;
  void sound ();
  public:
  dog::dog():s("Gav\n"){}
};

class cat:public Isound
{
  const char *s;
  public:
  cat::cat():s("Mjau\n"){}
  void sound () {printf(s);}
};

void dog::sound(void)
{
  printf("another virtual member called\n");
}

int main()
{
  Isound *f = new dog;
  f->sound();
  delete f;
  f = new cat;
  f->sound();
  delete f;
}
```

 

```
// C
#include <stdio.h>
#include <stdlib.h>

struct sound_t;
typedef struct sound_t
{
  const char *snd;
  void (*sound)(struct sound_t* s);
} sound_t;

void cat_sound_print(sound_t* s)
{
  printf("%s\n",s->snd);
}

void dog_sound_print(sound_t* s)
{
  printf("another virtual member called\n");
}

sound_t* new_dog(void)
{
  sound_t* rv=malloc(sizeof(sound_t));
  rv->snd="Gav!";
  rv->sound=dog_sound_print;
  return rv;
}

sound_t* new_cat(void)
{
  sound_t* rv=malloc(sizeof(sound_t));
  rv->snd="Mjay!";
  rv->sound=cat_sound_print;
  return rv;
}

int main(void)
{
  sound_t* f=new_dog();
  (*(f->sound))(f);
  free(f);
  f=new_cat();
  (*(f->sound))(f);
  free(f);  
  return 0;
}
```

 40 на С++, 47 на С.
Это если у тебя не развернуть функции, которые ты заботливо написал в одной строке.

с интерфейсами тоже пример нужен?
Кстати, такой момент. В детском примере, который в принципе не требует виртуалок и индиректов ты автоматом их применил.  Потому что привык их применять. И там где это нужно, и там где это не нужно. Это - психология С++, очень характерная.

----------


## homo ludens

> http://en.wikipedia.org/wiki/Standard_Template_Library


 А можно поподробнее, я имел в виду средства языка а не библиотеки.
Что такое STL я знаю.

----------


## Ull9

хорошо. 
мне нравится, и пож, последний пример.
ну а если так будет собака выглядеть

struct dog: Isound
{
dog(int s):i(s){}
void sound()
{
beep(1000,s); // 1000 Hz, s ms
}
}

----------


## Ull9

<<С чего бы это?
ты хочешь сказать, что в моем варианте нужна перекомпиляция? Или у меня менее прозрачное разбиение на компоненты, чем pimpl?>>
в с++ перекомпиляция точно так же ненужна, а ты утверждал что нужна.
_______
...Плюс отсутствие перекомпиляции при добавлении приватных методов/членов и соответственно более прозрачное разбиение на компоненты....
_______
против твоего "Плюс", я возражал.

интерфейсы и воид* это другой вопрос.

----------


## Ull9

> А можно поподробнее, я имел в виду средства языка а не библиотеки.
> Что такое STL я знаю.


 темплейт и есть средство языка. (или у тебя другие термины?)
а библиотеки это применение этих средств.

----------


## homo ludens

> хорошо. 
> мне нравится, и пож, последний пример.
> ну а если так будет собака выглядеть
> 
> struct dog: Isound
> {
> dog(int s):i(s){}
> void sound()
> {
> ...


 дай пожалуйста компилируемый пример, мне трудно разобраться что ты хочешь увидеть. Нету у тебя переменной i в родительском классе.
если ты имел в ввиду dog(int i):s(i){} то это не компилируется (именно из-за сохранности типов, которая тебе нравится).




> <<С чего бы это?
> ты хочешь сказать, что в моем варианте нужна перекомпиляция? Или у меня менее прозрачное разбиение на компоненты, чем pimpl?>>
> в с++ перекомпиляция точно так же ненужна, а ты утверждал что нужна.
> _______
> ...Плюс отсутствие перекомпиляции при добавлении приватных методов/членов и соответственно более прозрачное разбиение на компоненты....
> _______
> против твоего "Плюс", я возражал.
> 
> интерфейсы и воид* это другой вопрос.


 так, еще раз читаем мой пост.




> А для С изоляция может быть (и должна быть) более строгой, чем для С++.
> private виден в hh-файле, а typedef void* mytype не дает никакой информации о содержимом кроме интерфейса. Плюс отсутствие перекомпиляции при добавлении приватных методов/членов и соответственно более прозрачное разбиение на компоненты.
> На С++ это можно сделать только используя чистые интерфейсы, а не классы, что имхо более геморройно и провоцирует на множественное наследование интерфейсов.


 Поясняю и повторяю. В моей практике при использовании стандартных подходов перекомпиляция нужна в С++ всегда за исключением одного единственного случая. Реализации стабильного интерфейса абстрактным классом.
При pimpl (который есть С++ реализация того же void* т.е. попытка обойти необходимость полного определения структуры данных) перекомпиляция потребуется при любом изменении пабликов, не являющимися методами (и не надо говорить что в реальной жизни такие не используются).
При переведении из паблик в протектед или обратно членов класса (а наследование ты тоже приватным сделаешь?). И т.п.
Это заплатка, когда нет другого выхода, затратная по индиректам и бек-пойнтерам. Есть варианты, когда pimpl не потребует перекомпиляции, но они не полны, в отличие от интерфейсов.




> темплейт и есть средство языка. (или у тебя другие термины?)
> а библиотеки это применение этих средств.


 пример можно? желательно простой как твои кот с собакой. Полиморфизм без наследования средствами языка.
То, что генерик темплейт и полиморфный объект разные вещи я тоже знаю.

----------


## Ull9

mlja, pisal na kolenke.
eshhe raz dog

struct dog:ISound
{
void sound()
{
   beep(1000,s);
}
dog(int i=100):s(i) {}
private:
int s;
}

int main(), bez izmenenij.

----------


## Ull9

-----
Чего я еще не встречал, так это таких примеров. Забыть виртуальный деструктор - это завсегда, а вот проблем с void* - это извернуться надо.
------

zhivoj primer iz moej praktiki. posle sborki programmy, proishodit sledujushhee,
bluzhdajushij crash. programa krashitsja v raznyh mestah, inogda rabotaet stabilno, inogda crashitsja. prichina? neponjatno. iskali 3 dnja, brosili. vrode kak ne crashitsja, potom opjat' serija crash. prichinu nashli, prosmatrivaja rabotu s void*.
Logging rabotal s void*, poluchal void*, delal cast v konkretnyj ob'ekt. NO structura classa byla izmenena v module, a Logging modul' ob jetom ne znal. i delel cast v staruju structuru. posle chego inogda v nee pisal. platforma AIX IBM.

pimpl vs void*
ostavim v storone design i interfejsy, inogda net vozmozhnosti ih menjat'.
pimpl podhod tochno tak zhe i v tochno takoj zhe stepeni garantiruet otsutstvie perekompiljacii kak i void*. za iskljucheniem odnogo preimushhestva, type safe.
soglasen?

s tebja primer s novogo dog.

teper' template polimorfizm

class dog
{
const char *s;
public:
dog(const char* i): s(i){}
void sound()
{
printf(s);
}

class cat
{
int s;
public:
cat(int i):s(i){}
void sound()
{
void sound()
{
Beep(1000,s);
}
}

template <typename T>
void makeSound(T& t)
{
t.sound();
}

int main()
{
cat c;
dog d;
makeSound(c);
makeSound(d);
return 0;
}

----------


## homo ludens

> zhivoj primer iz moej praktiki.


 так тут не воид* виноват. Если ты определил что-то как воид* то это означает, что никаких преположений другие части программы не должны делать. Если им не доступен h-файл с детальным описанием типа.
У тебя получилось двойное описание одного типа данных. Т.е. где-то было два файла в которых один тип данных описывался по разному.
Такие вещи надо резать на стадии дизайна - это такой же источник глюка, как например дублируемый код или copy-paste программирование.

Смотри на воид* как на базовый тип всех указателей. Ты ведь можешь преобразовать в С++ без явного каста из dog в Isound? В с++ ты не можешь сделать обратное. А в С - можно в обе стороны.
Я лично слышал много пожеланий для С чтобы любой указатель мог бы быть преобразован в воид* без явного каста, но не наоборот. Если бы эти предложения прошли в стандарт, то ситуация была бы полностью идентична С++ c отношениемя родитель-потомок.
Но это не прошло, хз по каким мотивам, имхо по соображениям обратной совместимости.
С другой стороны мне например это никак не мешает - я очень редко использую конверсию между двумя typedef void* - у меня она означает скрытое наследование, которого надо избегать. Опять таки на стадии дизайна, а не программирования.




> pimpl vs void*
> ostavim v storone design i interfejsy, inogda net vozmozhnosti ih menjat'.
> pimpl podhod tochno tak zhe i v tochno takoj zhe stepeni garantiruet otsutstvie perekompiljacii kak i void*. za iskljucheniem odnogo preimushhestva, type safe.
> soglasen?


 Я был бы согласен если бы речь шла об интерфейсах против воид*.
А вот с pimpl не все так просто.


```
class pimpl
{
 public:
  int method1(int);
  int method2(int);
  int member1;
  int member2;
 private:
  class pimpl_private;
  pimpl_private* priv;
}
```

 Мы исходим из того, что в хорошо спроектированном интерфейсе методы (не их реализация, а только сигнатуры) стабильны и не меняются с развитием, они могут только добавляться, что в С++ делается через наследование.
А вот обработка внутренних состояний, типы и количество членов класса могут плыть во время разработки.

В моем примере мы видим два варианта.
1. есть member1 и member2. В 99% случаев это гарантия нестабильности определения класса. Любое их изменение/удаление/добавление вызовет перекомпиляцию. Более того, сам факт того, что они есть - нарушение pimpl и компромисс. Они должны быть в pimpl_private, а доступ к ним реализован через акцессоры get/set_member1/2 (чего никто из программеров увы не делает).

2. их нет и они перенесены в pimpl_priv.  Тогда чем эта реализация отличается от следующей?


```
// в hh-файле доступном коду
#define interface struct

interface notpimpl
{
  virtual ~notpimpl(void)	{}
  virtual int method1(void)=0;
  virtual int method2(void)=0;
};
// в hh-файле внутреннего определения
class notpimpl_realizz:public notpimpl
{
  int member1;
  int member2;
 public:
  virtual int method1(void);
  virtual int method2(void);
};
```

 вот в этом случае перекомпиляции не будет никогда, но это уже не pimpl - это именно интерфейсный класс и непонятно нафига тогда нужен pimpl?

Кстати и тут есть подвох, так как методы интерфейса не могут меняться, но могут добавляться по мере развития программы.
Если мы их добавляем прямо в определение notpimpl/notpimpl_realizz то перекомпиляция, но без нарушения работоспособности программы.
Без перекомпиляции надо делать наследника от notpimpl и новый класс. При этом старый код работает со старым h-файлом, а новый - с новым. И все ок.
В С решается похожим методом. Просто создается добавочный h-файл с добавочными интерфейсами, а старый код его не включает.

примеры кода и по поводу темплейтов в следующем посте, мне иногда работать тоже надо...

впрочем тупое решение (я никогда так не делаю, это на самом деле трюк и множественное наследование так не сделать)


```
#include <stdio.h>
#include <stdlib.h>

struct sound_t;

typedef struct sound_t
{
  const char *snd;
  void (*soundfnc)(struct sound_t* s);
} sound_t;

typedef struct
{
  sound_t sound;
  int z;
} dog_t;

typedef struct
{
  sound_t sound;
} cat_t;

void cat_sound_print(sound_t* s)
{
  printf("%s\n",s->snd);
}

void dog_sound_print(sound_t* s)
{
  printf("calling beep(1000,%d)\n%s\n",((dog_t*)s)->z,s->snd);
}

sound_t* new_dog(int i)
{
  dog_t* rv=malloc(sizeof(dog_t));
  rv->sound.snd="Gav!";
  rv->sound.soundfnc=dog_sound_print;
  return (sound_t*)rv;
}

sound_t* new_cat(void)
{
  sound_t* rv=malloc(sizeof(sound_t));
  rv->snd="Mjay!";
  rv->soundfnc=cat_sound_print;
  return rv;
}

int main(void)
{
  sound_t* f=new_dog(100);
  (*(f->soundfnc))(f);
  free(f);
  f=new_cat();
  (*(f->soundfnc))(f);
  free(f);  
  return 0;
}
```

 правильное решение здесь через интерфейсы, но это требует 4-х файлов, два из которых стабильны, а два - доступны пользователям для расширения. Однако тогда надо привести и твой код к такому же стандарту - разделив на стабильные модули и нестабильные. Я на пример не знаю, планируется ли создание еще наследников от Isound кроме dog и cat.
Если написать так как я пишу в реале плюс рассчитаю максимальную возможность расширения, то код будет более громоздкий, так как будет рассчитан на имитацию наследования композицией и работу с декларативными кусками (как таблица инициализации в моем примере с pthread_once).
Но это чуть позже. 
Работать надо.

----------


## Guffy

от вы тут о высоких материях, блин
в Ц "бытовуха" убивает в первую очередь


```
int bar(int lenght, char* a, char* b, char* c)
{
	return 0;
}

int fooC(int lenght)
{
	char* a;
	char* b;
	char* c;
	int res;

	res=-1;
	a=(char*)malloc(lenght);
	if(!a)
	{
		goto end;
	}
	b=(char*)malloc(lenght);
	if(!b)
	{
		goto freeA;
	}
	c=(char*)malloc(lenght);
	if(!c)
	{
		goto freeB;
	}
	res=bar(lenght, a, b, c);
	free(c);		
freeB:
	free(b);		
freeA:
	free(a);
end:
	return res;
}
```

 вместо


```
template<class T> class ArrayT
{
	T* buffer;
public:
	ArrayT(int lenght):buffer(0){
		buffer=new T[lenght];
	}
	~ArrayT() {
		if(buffer)
			delete[] buffer;
	}
	operator T*() {
		return buffer;
	}
};

int fooCPP(int lenght) throw(std::bad_alloc)
{
	ArrayT<char> a(lenght);
	ArrayT<char> b(lenght);
	ArrayT<char> c(lenght);
	return bar(lenght, a, b, c);
}
```

 "повбывав бы"

----------


## homo ludens

> от вы тут о высоких материях, блин
> в Ц "бытовуха" убивает в первую очередь


 А ты не убивайся.


```
int bar(int lenght, char* a, char* b, char* c)
{
	return 0;
}

int fooC(int lenght)
{
	char* a=alloca(length);
	char* b=alloca(length);
	char* c=alloca(length);
        return (a && b && c) ? bar(lenght, a, b, c) : -1;
}
```

 вообще fooC можно в одну строку написать, но лениво.

----------


## Ull9

дискуссия разрастается...

1. void* краш

то что произошло с программой это ясно. и то что это решается дизайном, тоже ясно, тут есть только один момент.
Что означает, что "эта проблема решается хорошим дизайном"? Дизайн это в том числе некие дисциплинарные методы, типа соглашений, типа запретов на копи/пейст. но там где соглашения всегда есть лазейка того, что кто то обойдет это соглашение. 
в с++, ситуация принципиально иная. с++, на уровне языка поддерживает type safe.Так что же лучше дисциюплина разработчиков или гарантия компилятора? 
уже столько раз это обсуждали, лучше компилятор.
только он даст гарантию о типах, а воид и есть тут дыра. и никакой дизайн неспасет от человеческой ошибки.

----------


## homo ludens

void* пример креша.

Компилятор в этом случае нашел бы ошибку конверсии типов. И сказал бы программисту, что так делать нельзя. А вот что сделал бы программист после этого? Исправил бы дизайн? Не верю. Программер уверен в том, что два типа описаных в разных местах - это один тип. То что эта информация неизвестна компилятору его не колышет - программер считает что все в норме.
И я могу поспорить - первое что он сделает - попытается обмануть компилятор. Например применив явный кастинг. Он решит проблему некомпилируемого кода, но не решит проблему неработающей программы - ему так проще, да и скажем откровенно - привычнее. 
Примеры обмана компилятора он видит каждый день - что такое pimpl, как не попытка обмануть компилятор? Почему бы ему этого не сделать?
А если более реальный случай - коды двух описаний типов писали разные люди? Тогда програмиста вообще не колышет ошибка компилятора - это не его зона ответственности и он считает себя вправе применитьь явный кастинг. В жизни оно именно так и происходит - сам видел.

"Если программа откомпилировалась без ошибок - ищи ошибки в компиляторе!" (с) 

Единственный выход - человек, который смотрит код подчиненных перед сборкой должен найти эту проблему дублированного описания сам. И никакой typesafe ему тут не поможет.

PS
C и С++ практически одинаковы по type-safe кроме приведения типов в отношении наследования. Т.е. здесь С даже строже, чем С++ - нет скрытых преобразований при наследовании.
Единственное исключение - С считает все указатели унаследованными от void* и поэтому при приведении к нему преобразования не требует. Нарушения сохранности типов здесь тоже нет - это вполне нормально. В С++ отношения родитель-предок строятся также.
Едиственное нарушение сохранности типов в С по сравнению с С++ - это разрешение явного преобразования из void* к любому указателю. Это удобно на практике, но действительно создает лазейки типа той которую ты описал. У меня нет определенного мнения на тему хорошо это или плохо для всех использующих язык С. Но  я знаю, что мне это пока не помешало ни разу, а помогало много раз. Кроме того в реальной жизни, если писать на С, избегая наследования то имхо это принципиального значения не имеет. Уровней наследования всего 2 и ошибки в коде типа

typedef void* type1_t;
typedef void* type2_t;
int type1_func(type1_t x);
...
type2_t y;
type1_func(y);

ошибки видны визуально сразу.

Другой вопрос, что если бы в С были средства не создания синонимов типов (typedef) а удобные средства создания новых типов - так чтобы type1_t и type2_t не приводились друг к другу без явного кастинга - это было бы неплохо.
Неудобные средства есть, например так:
#define typenew(x) struct { void* data; } x##_t;
Но я пока таким не пользовался, реальное удобство может показать только эксперимент - на него не всегда есть время.

----------


## Ull9

В моем примере мы видим два варианта.
1. есть member1 и member2. В 99% случаев это гарантия нестабильности определения класса. Любое их изменение/удаление/добавление вызовет перекомпиляцию. Более того, сам факт того, что они есть - нарушение pimpl и компромисс. Они должны быть в pimpl_private, а доступ к ним реализован через акцессоры get/set_member1/2 (чего никто из программеров увы не делает).
--------------------------------

tut ja neponjal sam dal primer i sam ego kritikuesh'
Nu jasnyj perez, na nado davat' publik chleny v klasse.

----------


## Ull9

Компилятор в этом случае нашел бы ошибку конверсии типов. И сказал бы программисту, что так делать нельзя. А вот что сделал бы программист после этого? Исправил бы дизайн? Не верю. Программер уверен в том, что два типа описаных в разных местах - это один тип. То что эта информация неизвестна компилятору его не колышет - программер считает что все в норме.

void* provociruet imenno takoj uproshhenyj podhod. o chem ja i govorju vse vremja

----------


## Ull9

Примеры обмана компилятора он видит каждый день - что такое pimpl, как не попытка обмануть компилятор? 

Net jeto obeshhanie kompiljatoru, o tom chto konkretnoe opisanie klassa budet dano pozzhe. jeto ne obman. esli kompiljatoru ne dat' obeshhanoe to on rugnetsja.

----------


## Ull9

Почему бы ему этого не сделать?
А если более реальный случай - коды двух описаний типов писали разные люди? Тогда програмиста вообще не колышет ошибка компилятора - это не его зона ответственности и он считает себя вправе применитьь явный кастинг. В жизни оно именно так и происходит - сам видел.

I ja videl javnyj cast. nu i kakoj ty delaesh' vyvod?
ja delaju takoj, cast, ljuboj est' priznak plohogo design (99%). nedarom v c++ on vygladit tak chtob ego mozhno bylo najti (static_cast<>...)
eshhe odin primer kak C stil' programmirovanija, a imenno cast, est' zlo. i priznak plohogo koda.

----------


## Ull9

Т.е. здесь С даже строже, чем С++ - нет скрытых преобразований при наследовании.
-----

ne ponjal 
pochemu on strozhe?
kakie u c++ skrytye preobrazovanija?
kakoe nasledovanie v C? 
davaj o terminah, chto ty ponimaesh' pod nasledovaniem?
dlja menja nasledovanija v C net. ili u tebja kakoe to svoe ponjatie o nasledovanii?

----------


## Ull9

Уровней наследования всего 2 и ошибки в коде типа

typedef void* type1_t;
typedef void* type2_t;
int type1_func(type1_t x);
...
type2_t y;
type1_func(y);

ошибки видны визуально сразу.
---------------------------
jeto esli oni v sosednih strokah. a v real'noj zhizni oni mogut byt' voobshhe v raznyh fajlah!
chto i bylo u nas v logginge.

Nu zachem jetot gemorr kogda C++ GARANTIRUET ih otsutstvie?

----------


## Ull9

privedennyj kod dlja poslednego varianta dog.
ne zanju kak drugim, no chitat' kod na C mozhno, no vygljadit on bolee urodlivej.
pri uslozhnenii primerov. (chem naverno zanimatsja nam i ne nado bolshe).
ochevidno, chto chitaemost' koda tol'ko uhudshaetsja.

----------


## Guffy

> А ты не убивайся.
> 
> 
> ```
> int bar(int lenght, char* a, char* b, char* c)
> {
> 	return 0;
> }
> 
> ...


 это вы мне будете в детском саду рассказывать и про alloca и про 1мб стека на поток (в винде по умолчанию) 
а насчет ansi C, если я правильно помню, то так нельзя


```
int fooC(int lenght)
{
	char* a=alloca(length);
	char* b=alloca(length);
	char* c=alloca(length);
        return (a && b && c) ? bar(lenght, a, b, c) : -1;
}
```

 можно так


```
int fooC(int lenght)
{
	char* a;
	char* b;
	char* c;

	a=alloca(length);
	b=alloca(length);
	c=alloca(length);
        return (a && b && c) ? bar(lenght, a, b, c) : -1;
}
```

 зы: в одну строчку и я писать умею, особенно если наплевать на правила хорошего тона в форматировании кода

----------


## Guffy

Второе, за шо я "повбывав бы", так вот таки за эти void*
Уже не времена, когда достаточно, текстового редактора и компилятора.
Нормальный разработчик, когда не клепает примеры на коленке, а работает с проектом, использует IDE и отладчик визуальный.
И что вы увидите в отладчике, когда по программе гуляют void* указатели?
Адрес указателя и хрен с маслом.  Каждый раз кастовать в watch? мазохизм.
А по типизированным указателям все ясно видно.
Да, я такой. Я люблю мягкое кресло и теплые тапочки

----------


## homo ludens

блин, пока сидел в работе набежало много постов...

по порядку.
pimpl - неважно, приватные члены или нет - при любом их изменении возникает перекомпиляция, так как они занимают место. Перекомпиляция возникает и при смене методов класса, но - методы класса всегда более стабильны чем члены.
Выход - переместить их в приватный класс и реализовать pimpl без членовы класса, любых - приватных или публичных, заменив акцессорами.
Если же pimpl-класс без членов, то он решает те же задачи, что и интерфейс, а тогда нафиг он нужен? Интерфейс намного гибче и расширяемей.

По поводу void* - блин, единственная дырка в типизации это возможность преобразования без каста из void* в указатель. Опять таки, полезно это или вредно - я не скажу. Мне это не мешает, но наверное есть пуритане, которым это не нравится. Я не молюсь на строгую типизацию и я не верю страшилкам, рассказываемым студентам, о том, что любое нарушение сохранности - абсолютное зло. Я знаю, что языки без сохранности типов позволяют намного более скоростную разработку программ, но на больших объемах кода дают дополнительный рост глюков. Нет здесь однозначного решения. Если бы оно было - запретили бы явные касты нафиг, но мне кажется, что такие языки нежизнеспособны.

Для меня возможность безкастового преобразования - просто синтаксический сахар. Примеры ошибок связанные с ними есть, но наверное можно накопать попытки взятия квадратного корня из HWND. Основные проблемы не здесь и не эта точка - самая болезненная в языках программирования.

Опять таки я пользуюсь статическими чекерами, которые дожны показывать большинство таких ошибок плюс те ошибки которые компилятор не поймает вообще. Возможно если пользоваться только компилером - все видится по другому.

Как воид* провоцирует подход по двойному объявлению типов - мне непонятно. Имхо программисту - программистово а архитектору - архитекторово. И код написанный программером должен обязательно читаться кем-то другим, хотя бы его напарником (если речь идет об XP). А уж не проверять интерфейсы, где двойное объявление видно сразу - гарантия проблем. Если уж на то пошло, то вещи на которые провоцирует С++ намного хуже - то же самое избыточное наследование например.
Вообще partially weak type в С обсуждалось до нас годами и будет обсуждаться после нас с достаточно вескими аргументами с обоих сторон. Я подозреваю, что мы сейчас просто повторяем какой-то из бесчисленных диалогов в инете. 
Кроме того избежать безкастового приведения в интерфейсах, (где typedef void* используется чаще всего) можно, просто никто так не делает, причем не по причине дурного тона, а потому что нет смысла - очень мала вероятность такой ошибки.
Кроме void* как абстрактный тип часто используется int в качестве хендла и пришлось бы резать и его, на всякий случай - вдруг кто-то сложит два файловых дескриптора, а потом передаст аргумент функции удаления потока. Я пока таких случаев не встречал, но вдруг...

По поводу уродливого кода - тут конечно на любителя. Мне например запись итератора кажется уродливой. Кроме того самый уродливый код и самые красивые решения я видел на http://www.ioccc.org/
Одна операционка в 32 Кб чего стоит.
Код программы, печатающий собственный текст (без доступа к сырцу) всегда выглядит уродливо, но эту простую программку оказывается не все выпускники могут написать. 

по поводу примера кода - там надо переработать и С++ код под интерфейсы. Иначе несправедливо выйдет - С код будет решать другие задачи - более широкие. Тот пример, что я дал - нехороший и так писать конечно нельзя.
Если есть время и настроение - можем сделать сравнение - ты свою реализацию на С++ я свою на С. Они конечно будут здоровыми и в пост не влезут. У меня на С  4 файла - два стабильных, с гарантией, что они не меняются никогда при любом дизайне и два - определяемых пользователем Isound/cat/dog. На С++ можно сделать в 3.

----------


## homo ludens

> это вы мне будете в детском саду рассказывать и про alloca и про 1мб стека на поток (в винде по умолчанию)


 Тогда не надо пользоваться динамическими переменными и аргументами функций. 
В том примере параметры не использовались для создания объекта, т.е. удовлетворяли всем требованиям автоматических переменных. Я так их и реализовал,  в GNU диалекте можно было бы написать и так.


```
int fooC(int lenght)
{
	char a[length];
	char b[length];
	char c[length];
        return bar(lenght, a, b, c);
}
```

 


> а насчет ansi C, если я правильно помню, то так нельзя


 можно.
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf
п. 6.7.8




> зы: в одну строчку и я писать умею, особенно если наплевать на правила хорошего тона в форматировании кода


 Правила хорошего тона в программировании называются coding style.
Я пользуюсь этим
http://www.gnu.org/prep/standards/standards.html
и немного
Linux kernel coding style (в любом ядре линукса есть файл с описанием)
Все, что там есть по этому поводу - ограничение длины строки и ограничение размера функции. Я их выполнил.
Вообще высокий уровень вложенности - это то, что отличает С-подобный синтаксис от бейсико-подобного, а оба этих синтаксиса покрывают большинство индустриальных языков.




> Второе, за шо я "повбывав бы", так вот таки за эти void*
> Уже не времена, когда достаточно, текстового редактора и компилятора.
> Нормальный разработчик, когда не клепает примеры на коленке, а работает с проектом, использует IDE и отладчик визуальный.


 Ну, последний раз я пользовался отладчиком даже не помню сколько лет назад. А уж IDE - на удаленном хосте или на машине заказчика... 
Так что тут ничего не могу сказать. 



> И что вы увидите в отладчике, когда по программе гуляют void* указатели?


 А уж что вы увидите в отладчике отлаживая недетерминированный код... Наверное год рождения моей бабушки.
И сильно в мультитриде отладчик помогает?



> Да, я такой. Я люблю мягкое кресло и теплые тапочки


 Я тоже люблю теплые тапочки, только вот когда по болоту идти надо - предпочитаю сапоги. А по болоту часто ходить приходится...

Имхо техника программирования, когда кодер вместо того чтобы продумать кусок кода а потом написать - гоняет цикл do {написал-запустил-посмотрел watch-исправил} while (watch переменная несовпадает с ожидаемой) дает намного больше глюков, чем любой void*.
Плюс непотопляемую уверенность в правильности этого куска кода и что виноват в глюке кто-то другой...
Однопоточные программы ушли, сейчас век недетерминированного кода...
Раньше было все проще...

----------


## homo ludens

уродливый код


```
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define _			;double
#define void			x,x
#define case(break,default)	break[O]:default[O]:
#define switch(bool) 		;for(;x<bool;
#define do(if,else)		inIine(else)>int##if?
#define true			(--void++)
#define false			(++void--)

char*O=" <60>!?\\\n"_ doubIe[010]_ int0,int1 _ Iong=0 _ inIine(int eIse){int
O1O=!O _ l=!O;for(;O1O<010;++O1O)l+=(O1O[doubIe]*pow(eIse,O1O));return l;}int
main(int booI,char*eIse[]){int I=1,x=-*O;if(eIse){for(;I<010+1;I++)I[doubIe-1]
=booI>I?atof(I[eIse]):!O switch(*O)x++)abs(inIine(x))>Iong&&(Iong=abs(inIine(x
)));int1=Iong;main(-*O>>1,0);}else{if(booI<*O>>1){int0=int1;int1=int0-2*Iong/0
[O]switch(5[O]))putchar(x-*O?(int0>=inIine(x)&&do(1,x)do(0,true)do(0,false)
case(2,1)do(1,true)do(0,false)6[O]case(-3,6)do(0,false)6[O]-3[O]:do(1,false)
case(5,4)x?booI?0:6[O]:7[O])+*O:8[O]),x++;main(++booI,0);}}}
```

 рисует график полиномиальной функции - коэффициенты в командной строке (например 0 0 1 нарисует параболу).
Взято с http://www.de.ioccc.org/whowon.html
Понятно, что игрушка, только вот на С++ я такого не видел... 
Код операционной системы сюда постить не буду - там целых 166 строк кода...

----------


## Guffy

> Тогда не надо пользоваться динамическими переменными и аргументами функций.


 А никто не обещал, что 3*lenght меньше 1мб  
фишка и была в том, что размер заранее неизвестен и как весело это выглядит с malloc/free




> можно.
> http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf
> п. 6.7.8


 ладно, тут побороли  :smileflag:  




> И сильно в мультитриде отладчик помогает?


 Нормально помогает, не беспокойтесь  
Все при бряке потоки тормозятся и над каждым можно провести показательные пытки (стек, локальные переменные в каждом фрейме) 




> Имхо техника программирования, когда кодер вместо того чтобы продумать кусок кода а потом написать - гоняет цикл do {написал-запустил-посмотрел watch-исправил} while (watch переменная несовпадает с ожидаемой) дает намного больше глюков, чем любой void*.


 А Вы, стало быть, написав 200-300 строк кода всегда попадаете в десятку?  1-2 глючка в некоторых случаях на такое кол-во строк, написанных сходу - это не преступление. У уж лучше "увидеть",  чем "втыкать" (или натыкивать printf где надо и не надо). 



> Плюс непотопляемую уверенность в правильности этого куска кода и что виноват в глюке кто-то другой...


 Уверенность и вышеописанный цикл - это ортогональные вещи. одно другому не мешает.




> Однопоточные программы ушли, сейчас век недетерминированного кода...
> Раньше было все проще...


 Многопоточный становиться недетерменированным, если плвать в знаниях и не иметь опыта. Иначе - не так страшен черт...

----------


## homo ludens

> А никто не обещал, что 3*lenght меньше 1мб  
> фишка и была в том, что размер заранее неизвестен и как весело это выглядит с malloc/free


 переменные временные, наружу не экспортируются, так что все ок.
А если используешь временные переменные больше 1Мб - ну, тут уже проблемы с логикой и дизайном, имхо. Переполнить стек можно не только через alloca.



> Нормально помогает, не беспокойтесь  
> Все при бряке потоки тормозятся и над каждым можно провести показательные пытки (стек, локальные переменные в каждом фрейме)


 В мультитриде основной глюк - race condition. Вот он не ловится ничем.
Я видел сервер, который два года работал без проблем на одной машине. А когда переставили на другую (с перекомпиляцией) -  сдох.
Временные проскальзывания между тридами поменялись и попали на критический участок. Вот и весь недетерминизм. И отладчик тут не поможет, только логгирование участков кода и запуск на стресс-тестах - пару недель минимум в цикле плюс случайные временные задержки в разных потоках. И то хрен помогает.
Сам сколько раз видел - сидит чел и видит в дебаггере все ок. А без дебаггера - креш. И понять не может почему так. 




> А Вы, стало быть, написав 200-300 строк кода всегда попадаете в десятку?  1-2 глючка в некоторых случаях на такое кол-во строк, написанных сходу - это не преступление. У уж лучше "увидеть",  чем "втыкать" (или натыкивать printf где надо и не надо).


 Я всегда пишу сначала интерфейсы, а только потом имплементацию.
А потом - тест-модуль.
И вот потом, запуская код сравниваю выходы теста с ожидаемыми.
Что я могу в дебаггере - посмотреть массив 1Мб? Я лучше его выведу в файл и сделаю diff с образцом - это лучше покажет ошибки.
А если на моей машине работает, а на удаленной нет? Я запущу тот же make test, а вот с дебаггером там смотреть может быть проблематично.

А если программка должна считать 2 недели на кластере из 4-х компов?
Сидеть все это время перед экраном?
Здесь вообще только логгирование помогает. А если сервер работает 24x7   и надо гонять стресс-тесты для определения MTBF? Откуда я знаю сколько раз он за неделю стресс-теста дал отказ в сервисе? Из дебаггера?

Дебаггером пользуюсь в среднем два-три раза в год в следующих ситуациях.
1. На машине заказчика креш. Тогда для анализа core dump можно взять gdb. Давно такого не было, так как практически все core dump отлавливаются valgind'ом на стресс-тестировании.
2. На машине заказчика или у меня при тесте подвисание из-за дидлока. Вот это бывает. Тогда аттачу дебаггер и делаю снимок стека всех потоков (а их может быть сотня) в текстовый файл. И на нем сразу видны точки deadlock. 

Единственное, что у клиента должна быть версия откомпиленная с отладочной информацией. Клиенты обычно не протестуют - это условие поддержки.




> Уверенность и вышеописанный цикл - это ортогональные вещи. одно другому не мешает.


 Эх если бы так было...  Стандартная отмазка - в дебаггере все работает. 




> Многопоточный становиться недетерменированным, если плвать в знаниях и не иметь опыта. Иначе - не так страшен черт...


 Многопоточный код на языках без поддержки многопоточности всегда недетерминирован. Так как триды между собой не синхронизированы есть вероятности общего обращения к куску памяти или канселляции потока в неподходящий для него момент или рекурсивной блокировки и т.п. И на разных машинах и при разных условиях это будет всегда по разному.
И ошибки такого типа отлавливаются очень тяжело. Забытый программистом мютекс может вылезти потом через год эксплуатации.
А дебаггер покажет, что все в порядке.

----------


## Guffy

> Многопоточный код на языках без поддержки многопоточности всегда недетерминирован. Так как триды между собой не синхронизированы есть вероятности общего обращения к куску памяти или канселляции потока в неподходящий для него момент или рекурсивной блокировки и т.п. И на разных машинах и при разных условиях это будет всегда по разному.
> И ошибки такого типа отлавливаются очень тяжело. Забытый программистом мютекс может вылезти потом через год эксплуатации.
> А дебаггер покажет, что все в порядке.


 Вот тут мы и расходимся. 
Несколько простых правил, и все будет пучком.
Ну, например:
1. Общие ресурсы всегда внутри мютекса (или критической секции, как у нас, "виндузятников"). Причем держать нужно не меньше, но и не больше чем нужно.
2. Стараться избегать всеми возможными способами блокировать больше 1го ресурса за раз. Если уж надо 2 - сидеть, думать.
3.  Потоки стараться принудительно не прибивать, а взводить им соотв. евент - "тормози, братан, приехали" - и сам поток делает красивый ретурн из поточной функции
Ну и как всегда  - опытный мен на ЦПП в 99,9% случаев просто не теряет ни памяти, ни мютексов. Культура пользования автоматическими обертками к ресурсам просто практически не оставляет лазеек.
зы: рекурсивные блокировки - критические секции позволяют повторный вход того же потока.

----------


## homo ludens

дык это все понятно и алфавитная сортировка блокировок и все остальное делается. И продумывать код надо.
Проблема-то в другом, если ошибка программиста в сложном коде, то черта с два ее отловишь за разумный промежуток времени. И даже стресс-тесты не очень помогают.
И средств автоматических отлова почти нет. Hellgrind есть, но у него много ложных срабатываний.

А потом у клиента сто пудов всплывет - проверено.

А насчет флага - это конечно хорошо, сам стараюсь так делать, но если только поток не занят в блокирующем вводе-выводе. Тут приходится канселляцию делать с очисткой ресурсов и прочей фигней. Крайне гемморойная вещь. И обвертки тут не очень помогают.
У меня вот поток базу данных читает и асинхронно к ней никак не обратиться. 

Вообще на десятке потоков и на одном-двух компах недетерминированность не особо заметна, а вот как под сотню потоков переваливает и на разных компах, да на двухядерных... 

PS
рекурсивные мютексы вроде непереносимы. На линухе я их юзаю в полный рост, а вот если под что другое надо будет портировать - хз что делать.
А жаль, удобная штука. Хотя слышал, что очень затратны, по сравнению с обычными.

----------


## Guffy

> А насчет флага - это конечно хорошо, сам стараюсь так делать, но если только поток не занят в блокирующем вводе-выводе. Тут приходится канселляцию делать с очисткой ресурсов и прочей фигней. Крайне гемморойная вещь. И обвертки тут не очень помогают.
> У меня вот поток базу данных читает и асинхронно к ней никак не обратиться.


 И тут они (обертки, возможно несколько другого вида) очень даже помогают. Если заранее известно, что поток будет работать с таким ресурсом, что иногда прийдется тупо прибивать функцию потока и остаток ее не выполнится (и соотв. не вызовутся деструкторы локальных переменных), то для такого потока пишется специальный класс потока, в котором все ресурсы - поля класса. И функция потока не должна оперировать подобными ресурсами на стеке - только пользоваться полями класса. В итоге за освобождение отвечает дестуктор объекта поток.  
Либо ресурсы управляются обертками типа "смартпоинтер" и "хорошем" случае подчищаются функцией потока при выходе, в "плохом" - деструктором объекта поток (т.е. если например, деструктор потока видит, что в одном из полей мютекс, и функция потока при выходе его не разлочила - от его отпускает). 
И тут нам ЦПП помогает

----------


## Ull9

я позволю себе реплику здесь.
все эти обертки, с которыми наш оппонент согласился в С просто несуществуют, т.к там деструктора нет.

----------


## Guffy

вот этими простыми "бытовыми" удобствами я его и добиваю

----------


## homo ludens

здесь есть ряд спорных моментов, хотя возможно я что-то не понимаю в С++ реализациях этого дела.

Во первых при канселляции потока через pthread_cancel в точке канселляции в режиме DEFERRED (напрмер ожидании синхронного файлового ввода типа read socket) деструкторы автоматических переменных вызываться не могут - канселляция тупо очищает стек и убивает трид. Возможно что в С++ есть высокоуровневые методы обхода этого, однако мне они неизвестны.
В С я об этом не беспокоюсь, так как деструкторов нет, а очистка стека - нормальная процедура при выходе из функции. В С++ у меня гарантированно останется мусор в памяти, либо я должен отказаться от локальных смарт-пойнтеров и т.п. Все те мелкие радости которые дает обвертка динамической памяти в автоматическую переменную здесь становятся крупными пакостями и то, что будет хорошо работать в try-cath блоке сдохнет в таких функциях.

Более того, я не понимаю как можно вообще корректно удалить автоматическую переменную с деструктором (тот же смарт пойнтер) в такой ситуации. 
А жить в таких функциях без локальных переменных имхо очень тяжело - писать все на динамике - ну нафиг. 

Единственная реальная проблема здесь - динамическая память, удаление которой делается через pthread_cleanup. Способ не самый удобный, хотя по сути представляет собой тот же деструктор.
Неудобство заключается в том, что точек канселляции может быть (и обычно бывает) несколько и на каждую из них надо писать свой cleanup handler, так как ресурсы могут быть распределены в этих точках по разному, а контролер ресурсов сильно утяжеляет трид.

Кстати, alloca я использую в основном в функциях, где возможна канселляция - позволяет уменьшить код cleanup handler, а то и вообще его избежать.

Во вторых обвертка для потока - тоже вещь не самая прозрачная. Очевидно, что в случае канселляции деструктор обвертки не может выполнятся в стеке собственного потока  - т.е. возникает менеджер потоков. Если же трид хочет убить себя корректно, то команду этому менеджеру он должен отослать асинхронно - возникает очередь сообщений между всеми потоками и менеджером. либо должно быть два варианта деструктора - для вызова в стеке потока (например через pthread_join) и для внешнего с канселляцией. Т.е. геморрой есть в любом случае и имхо языковые конструкции тут мало что дадут.

Кстати, деструктор при канселляции разлочить мьютекс никак не может - этот мьютекс привязан к триду и чтобы его разлочить деструктор должен выполняться в том же потоке, а это при канселляции никак не возможно.

Впрочем про высокоуровневые средства С++ для мультипотока мне ничего неизвестно, я работаю с POSIX интерфейсом и там все так как я описал. Возможно что я что-то не знаю в современном С++.
С другой стороны средства работы с потоками предоставляет ОС и они от языка вроде мало зависят.

----------


## Ull9

pthread_cancel, насколько я понимаю довоьно грубая и сильная вещь. конечно из пушки по воробьям можно и столбы повалить. можно например abort(), вызвать,а потом сетовать, что деструкторы не сработали.

я не пользуюсь ПОСИКСо, как по мне это сильно муторно с этим АПИ работать.
из-за слишком низкоуровневого АПИ, сложно разглядеть логику. И не надо меня убеждать, что больше контроля. Сколько нужно контроля столько я и беру. 
что можно отдать компилятору, деструкторам, врапперам, отдаю им. они не ошибаэтся, в отличие от меня.
как, ты верно подметил существуют очень хорошие обeртки вокруг этого дела.
Они учитывают и посикс и платформозависимые вещи тоже, так что код получается квазипортабельным.
Я пользуюсь АСЕ, классая вещь, все есть и треды и атомик переменные и сокеты, болле сложны, например проактор. И все обьектами. и нет никаких проблем о которых ты писал.

----------


## Ull9

> блин, пока сидел в работе набежало много постов...
> 
> по порядку.
> pimpl - неважно, приватные члены или нет - при любом их изменении возникает перекомпиляция, так как они занимают место. Перекомпиляция возникает и при смене методов класса, но - методы класса всегда более стабильны чем члены.
> Выход - переместить их в приватный класс и реализовать pimpl без членовы класса, любых - приватных или публичных, заменив акцессорами.
> Если же pimpl-класс без членов, то он решает те же задачи, что и интерфейс, а тогда нафиг он нужен? Интерфейс намного гибче и расширяемей.


 при любом подходе, измение кода влечет перекомпиляцию. ЛЮБОМ, иначе как код будет изменен? Вопрос только в том что пимпл и интерфейсы позволяют УМЕНьШИТь обем перекомпиляции. далее. интерфейс не всегда возможен. Например прямо сейчас у меня контракт, в котором сказано maintenance, понятно? не могу я код перелопачивать! НЕМОГУ. максимум, что могу делать forward declaration or pimpl.



> Я знаю, что языки без сохранности типов позволяют намного более скоростную разработку программ, но на больших объемах кода дают дополнительный рост глюков.


  О, что и следовало доказать. На больших программах рост глюков. А маленьких, я к слову сказать уже лет 10 не видел 



> Как воид* провоцирует подход по двойному объявлению типов - мне непонятно. Имхо программисту - программистово а архитектору - архитекторово. И код написанный программером должен обязательно читаться кем-то другим, хотя бы его напарником (если речь идет об XP).


  Увы, в реальной практике, почти всегда нехватает людей, которые будут проерять твой код. Только тестер. и то, он же не код проверяет.

----------


## homo ludens

> pthread_cancel, насколько я понимаю довоьно грубая и сильная вещь. конечно из пушки по воробьям можно и столбы повалить. можно например abort(), вызвать,а потом сетовать, что деструкторы не сработали.


 pthread_cancel может быть вполне безопасен, просто не надо объявлять трид с асинхронным канселлированием (это действительно похоже на abort), а с режимом DEFERRED. Это означает, что канселляция возможна только в точке проверки через pthread_testcancel или в процедуре синхронного файлового ввода/ввода. Т.е. техники вполне безопасны если их правильно применять. Однако как ты правильно заметил - муторны. 
Правда, как я понимаю это единственный способ остановить поток в ожидании с очисткой ресурсов. Или надо вешать контроллер ресурсов, вещь в принципе возможную, но очень тяжелую, т.е. каждое распределение/удаление памяти надо заносить в список ресурсов и потом при убийстве потока их чистить. В реалтаймовых системах это может быть накладно. Опять таки в самом контроллере ресурсов надо блокировать канселляцию для обеспечения атомарности - в общем система та еще получается.

С другой стороны мне непонятно вот что. Есть обвертка чужого кода, которая юзает POSIX, и я хочу этой обверткой убить трид. Причем этот трид в этот момент висит допустим в синхронном чтении и чтение это не обернуто, а просто мне в триде захотелось допустим сделать getline из файлового дескриптора или sleep или еще что-то затратное по времени и неизвестно когда завершающееся.
Каким образом обертка узнает какие ресурсы я в этот момент распределил и какие надо убить? 
Вот пример


```
void func(void)
{
  foo f;
  bar *b=new bar;

  sleep(100000);
  delete b;
}
```

 Убить такой поток не дожидаясь конца слипа можно только канселом. Если есть доплнителные возможности операционки, тогда может существует другое решение, но под линуксом например стадартная библиотека - POSIX. Подозреваю что под AIX - тоже. 

Но в этом случае не выполнятся деструкторы f и b, хотя память под f будет очищена.
Для b можно придумать способ через контроллер ресурсов обвертки. Т.е. конструировать объект через factory связанный с обверткой потока либо просто анализировать в конструкторе базового типа bar pthread_self и заносить в таблицу распределенных ресурсов.
А вот что делать с foo? Я не знаю метода, который бы позволил корректно выполнить деструктор в такой ситуации, причем это никак не зависит от наличия или отсутствия обвертки (если она использует POSIX, а это все-таки стандарт). Даже если оборачивать все sleep/read/recv/getline и т.п. другими функциями и пользоваться только высокоуровневыми вызовами.




> Я пользуюсь АСЕ, классая вещь, все есть и треды и атомик переменные и сокеты, болле сложны, например проактор. И все обьектами. и нет никаких проблем о которых ты писал.


 Про ACE слышал очень много хорошего, однако сам с ним не успел поработать. С другой стороны в годы увлечения CORBA пытался запустить TAO (их смежный продукт) - увы, мне не удалось его даже скомпилировать. Сейчас наверное ситуация изменилась к лучшему, но тогда (2002 +-год) это был продукт выглядевший очень сыровато.

----------


## Ull9

так как сформулирован пример, тут тупик.красивого решения нет.
если у меня в коде такая потребность существует, то я применяю принципиально другой подход. Есть на такие случаи два паттерна реактор и проактор. (обакстати уже реализованы в АСЕ, причем на почти всех платформах). Суть в следующем.
Если я что то жду, допустим чтение из файла, или сообщение по сокету, или вообще нечто асинхронное, то создаю обьект, регистрирую его в проакторе (запускаю в нем, для его нужд, 4 треда (по числу процессоров)), передаю в проактор асинхронный ресурс, например хендл файла, и хопля, проактор меня сам известит, через колл бэк интерфейс, что чтение строки в файле произошло.
и Я ПОЛУЧАЮ УЖЕ ПРОЧИТАННУЮ СТРОКУ.

надеюсь я понятно выразился.

теперь к твоей проблеме, которую ты спрашивал, у меня теперь нет проблемы убивать тред. если мне надо срочно остановится, я посылаю команду проактору, а он, уже сам решает как ему аккуратно это сделать. Т.е. эту головную боль я делегировал ему. и он там сам у себя внутри трахается с посиксом, или с другими платформенно зависимыми функциями. И если надо внутрь заглянуть, нет проблем, код открыт.
У меня своих проблем хватает, я допустим бизнесс логикой займусь.

С ТАО, тоже занимался, капризная вещь, но запустил потом ноу проблем.

----------


## Guffy

```
class DisposeableThread;

class Disposeable
{
 Disposeable* prev;
 Disposeable* next;
public:
 Disposeable():prev(0), next(0)  { 
  //.code..
 }
 ~Disposeable() { 
  //.code..
 }

 friend DisposeableThread; 
}

class DisposeableThread
{
 Disposeable* first;
 class DisposeablePtr
 {
  DisposeableThread* tr;
 public:  
  Disposeable* ptr;
  DisposeablePtr(DisposeableThread* t, Disposeable* d):tr(t), ptr(d) {
    if(tr->first) 
     tr->first->prev=ptr; 
    prt->next=tr->first;
    tr->first=prt;
  }
  ~DisposeablePtr(){ 
    if(ptr->prev) {
     tr->first=ptr->prev;
     ptr->prev->next=ptr->next;
    }
    if(ptr->next)
     ptr->next->prev=ptr->prev;
    delete ptr; 
  }
 }
public:
 DisposeableThread():first(0) {
 }
 ~DisposeableThread() {
   while(first) {   
    Disposeable* t=first;
    first=first->next;
    delete t;
  }
 }
 void ThreadProc() {
   DisposeablePtr resource1(this, new Disposeable());
   Sleep(10000);
   {
    DisposeablePtr resource2(this, new Disposeable());
    Sleep(10000);
    {
     DisposeablePtr resource3(this, new Disposeable());
     Sleep(10000);
    }
    Sleep(10000);
   }
   Sleep(10000);
 }
}
```

 На самом деле, возможно, стоит чуть сложнее, но в целом идея, надеюсь, ясна?

----------


## homo ludens

> если у меня в коде такая потребность существует, то я применяю принципиально другой подход.


 Ты делаешь совершенно правильно, кроме того современный код обычно использует неблокирующие вызовы либо aio_read/write, которые  решают много проблем и дают плюс к производительности. Хороший обзор, хотя немного устаревший - http://www.kegel.com/c10k.html - так называемая проблема с10k - 10000 клиентов на сервер.
У меня проблема возникала в том, что интерфейс к базе данных в потоке мог выполнять запрос очень долго и если надо было убивать поток срочно, то приходилось делать такие трюки (ту систему делал на С++). Асинхронных запросов к БД увы не встречал.

Тем не менее я думаю, что даже в ACE, в модели трид на клиент, если ты в клиентском триде дашь например что-то типа  fgets(stdin) или sleep(MAX_INT-1) и попытаешься убить трид в этот момент, то есть только два варианта - либо трид не убьется и будет ждать завершения, либо убьется с потерей ресурсов. Другой вопрос, что это редко когда надо, поэтому на эти грабли практически никто не наступает.




> У меня своих проблем хватает, я допустим бизнесс логикой займусь.


 Юзать готовые крупные библиотеки класса ACE и заниматься только бизнес-логикой конечно удобней, И я пробовал такой подход, только у меня на системы 24х7 контракт на поддержку и развитие - на время жизни системы. И если меня будят в 3 часа ночи через год после сдачи проекта с требованием восстановить работоспособность за 10 минут, то времени на звонки в саппорты нету, а свой код знаешь всегда. Оказалось проще написать свой вариант, не такой удобный и универсальный, но зато ряд проблем ушло навсегда.
Хотя конечно это тоже решение уродское, изобретать велосипед, но было много раз, когда чужая толстая компонента вдруг перестает поддерживаться или вообще фирма меняет владельца или из нее уходят спецы, писавшие компоненту - и все, уже не разберешься в проблеме за разумное время. Причем это было как с open-source, так и с коммерческим саппортом. Ну нафиг, лучше спать спокойно.




> [CODE]
> 
> На самом деле, возможно, стоит чуть сложнее, но в целом идея, надеюсь, ясна?


 Это то, что я называл контроллером ресурсов. Имхо это сильный изврат и писать так очень тяжело. Кроме того есть ряд функций, которые могут вызываться и из главного трида и из убиваемых потоков. Т.е. можно придти к тому, что всю программу придется так писать.

На С код выглядит намного проще



```
void *thread_func(void* p)
{
  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,0);
 ...
  intfunc();
 ...
 return 0;
}

void intfunc(void)
{  
  struct x1 x1;
  struct x2 *x2=malloc(sizeof(struct x2));
  pthread_cleanup_push(cleanup,x2);
  int __pt;
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&__pt);
  read/sleep/pthread_testcancel - любой длительный процесс допускающий канселляцию.
  pthread_setcancelstate(__pt,0);
  pthread_cleanup_pop(1);

  cleanup(x2);

}

void cleanup(void* p)
{
  if(p)
   free(p);
}
```

 Хотя конечно тоже всех проблем не решает, так как если в канселлируемом участке идет не атомарный вызов, а вызов функции чужой компоненты (например запрос к БД), то я не знаю какие ресурсы размещаются там внутри. Например если будет getline вместо read - случайный memory leak гарантирован и тут уже ничего не поможет, нет способов это решить.
А в 24х7 любой memory leak будет накапливаться и в конце концов убьет систему.

----------


## Guffy

а шож вы хотели? ресурсы в потоке - не сахар. как заказывали, так и получили.
тут либо так, либо потоку передается указатель на структуру (объект) передавать и 
поток только с полями и работает, накаких локальных ресурсов на стеке.

А в вашем коде - это заслуга не Ц ни в коем разе. 
Это реализация птридов вам помогает повесить фунцию очистки.
А моя реализация- делает все сама.
На самом деле вы меня натолкнули на мысль упростить по методике стека


```
class DisposeableThread;

class Disposeable
{
 Disposeable* next;
public:
 Disposeable():next(0)  { 
  //.code..
 }
 ~Disposeable() { 
  //.code..
 }

 friend DisposeableThread; 
}

class DisposeableThread
{
 Disposeable* first;
 class DisposeablePtr
 {
  DisposeableThread* tr;
 public:  
  Disposeable* ptr;
  DisposeablePtr(DisposeableThread* t, Disposeable* d):tr(t), ptr(d) {
    prt->next=tr->first;
    tr->first=prt;
  }
  ~DisposeablePtr(){ 
     tr->first=ptr->next;
    delete ptr; 
  }
 }
public:
 DisposeableThread():first(0) {
 }
 ~DisposeableThread() {
   while(first) {   
    Disposeable* t=first;
    first=first->next;
    delete t;
  }
 }
 void ThreadProc() {
   DisposeablePtr resource1(this, new Disposeable());
   Sleep(10000);
 }
}
```

 обратите внимание на саму ThreadProc, а все астальное считайте моей реализацией птридов

----------


## Ull9

спасибо за похвалу. разумеется подход правильный. и если использовать не доморощеные велосипеды а стандартные, то другому программисту разобратся значительно легче, или даже велика вероятность, что он АСЕ  уже знает по другим проектам

теперь об этом



> Тем не менее я думаю, что даже в ACE, в модели трид на клиент,


 я уверен, что такой подход ущербный, а если будет 1000 клиентов, а если больше?
начиная с какого-то числа, система просто остановится, треды задохнутся.
нет, нам такое предлагать ненадо.

----------


## homo ludens

> при любом подходе, измение кода влечет перекомпиляцию. ЛЮБОМ, иначе как код будет изменен? Вопрос только в том что пимпл и интерфейсы позволяют УМЕНьШИТь обем перекомпиляции. далее. интерфейс не всегда возможен. Например прямо сейчас у меня контракт, в котором сказано maintenance, понятно? не могу я код перелопачивать! НЕМОГУ. максимум, что могу делать forward declaration or pimpl.


 Тут перекомпиляция на самом деле вопрос второй. Интерфейсы позволяют уменьшить связность кода, т.е. зависимости между компонентами. Как одно из следствий - уменьшение времени перекомпиляции. Но напрмер есть две команды программеров написавшие свои компоненты и если изменение куска кода одной командой влечет за собой изменение общего для них h-файла, то ковыряться в коде придется обоим. А если изменения интерфейса нет - то только одной.
В сильносвязанном проекте любой пук одного человека может вызвать правку кода всеми остальными.
Поэтому и требуется изоляция.
И еще пример. Система, которую нельзя останавливать. Но можно выгрузить динамическую библиотеку и подгрузить новую. Очень удобно, но требует железно стабильного интерфейса.

Если у нас есть интерфейс компоненты как набор функций и мы их меняем - то работа начинается у всех. Следовательно методы интерфейса должны быть самой стабильной частью системы. 
А если добавляются методы - имхо это нормальная практика при росте системы? В С перекомпиляции всего проекта нету, нужно перекомпилировать только те модули которые используют новые методы. А в С++ перекомпиляция возникает если методы - виртуальные функции, тогда физический размер унаследованного от интерфейса класса меняется. Но только перекомпиляция, другие команды разработчиков использующие старый интерфейс свой код не меняют - только перекомпилируют.

Теперь посмотрим что будет если у нас межкомпонентное взаимодействие реализовано в виде обычного класса с состоянием и методами. По моему опыту вероятность что в ходе разработки  поменяется определение внутреннего состояния велика, больше чем вероятность изменения сигнатур методов.  Поэтому из этого класса желательно убрать все члены класса, пофиг, приватные или паблик, заменив их акцессорами, которые переадресуются в методы некоторого универсального указателя внутри класса. И вот пока во внешнем классе нет виртуальных функций, этот метод оказывается самым неудобным, но самым практичным, так как реализует наименьшую связность компонент. Т.е. стабильность кода не пострадает если я изменю типы его внутренних переменных. Стабильность кода не пострадает если я  добавлю методы, если они не виртуальные. А вот добавление виртуальных методов вызовет перекомпиляцию всего проекта, а не только компоненты, хотя стабильность кода при этом не меняется.
Вот все что я описал - это собственно и есть pimpl, как я его понимаю.

Если у тебя maintence, то убрать существующие члены класса ты не можешь - придется много лазить по чужому коду. Здесь pimpl хорош, но опять таки пока не начинают добавляться  виртуалки. Тогда - опять перекомпиляция. 
А если пишется новый код с нуля?
Вроде из абстрактных соображений идеальным решением был бы абстрактный класс-интерфейс без состояний, реализующий factory для разных объектов, представленными в свою очередь тоже интерфейсами. 
Но без виртуалок. А таких абстрактных классов не бывает - это уже что-то другое. Поэтому и возникает техника pimpl.

В С pimpl не требуется - опять таки нет поддержки виртуалок компилятором и абстрактный интерфейс - просто набор функций, включая конструкторы/инициализаторы и деструкторы. А если нужны виртуалки - реализуются индиректами и интерфейс никак не трогают - они часть внутреннего состояния. И в С межкомпонентное взаимодействие оказывается намного проще чем в С++. 

Вообще, если меня склероз не подводит, то предшественником pimpl были d-pointers, используемые в Qt. И основной их мотивацией было как раз снизить время компиляции и зависимость между компонентами.

У меня есть такое четкое визуальное представление о С как двумерном языке, а С++ - трехмерном. И это новое измерение, добавляемое ООП и концентрацией на типах, а не на flow как будто только добавляет возможности, но именно из за их обилия - решать задачу иногда становится сложнее. 
Я вообще не от хорошей жизни из С++ ушел.

----------


## Ull9

в принципе верно. согласен. 
прости но это как бы тривиальные вещи. и интрерфейсы, и пимплы,
Началось как бы с того что пимпл перед воид имет одно преимущество - отсутствие воид.
Ну а то что интерфейсы лучше пимпл, ясный перец, что лучше. ОБ ЭТОМ спора небыло.

а вот почему из ц++ ушел в с, непонятно, эдак можно следующим языком ассемблер выбрать.

----------


## homo ludens

> а шож вы хотели? ресурсы в потоке - не сахар. как заказывали, так и получили.
> тут либо так, либо потоку передается указатель на структуру (объект) передавать и 
> поток только с полями и работает, накаких локальных ресурсов на стеке.


 т.е. про exception - забыть. Про неатомарные автоматические переменные забыть. Про временные объекты забыть.
Писать на С++ забыв вообще про все кроме динамических ресурсов - это тяжеловато, имхо.




> А в вашем коде - это заслуга не Ц ни в коем разе. 
> Это реализация птридов вам помогает повесить фунцию очистки.


 заслуга С здесь в том, что работа с автоматическими переменными не зависит от того, в потоке я работаю или еще где-то. Если я пишу функцию, мне пофиг, где она выполняется, единственное ограничение - долгие вызовы я должен обрамлять скобками разрешающими и запрещающими канселляцию.

А функцию очистки можно и в С++ юзать, проблем нету.
Кроме того, если делать глобальный контроллер ресурсов, то лучше юзать pthread_key и pthread_get/setspecific. Там автоматом все очищается, своего рода общий деструктор потока.
или вообще писать __thread char x[200];
Просто точка срабатывания редкая, потому проще локально определить cleanup, чем следить за всеми ресурсами в потоке.

----------


## Guffy

> т.е. про exception - забыть. Про неатомарные автоматические переменные забыть. Про временные объекты забыть.
> Писать на С++ забыв вообще про все кроме динамических ресурсов - это тяжеловато, имхо.
> 
> 
> 
> заслуга С здесь в том, что работа с автоматическими переменными не зависит от того, в потоке я работаю или еще где-то. Если я пишу функцию, мне пофиг, где она выполняется, единственное ограничение - долгие вызовы я должен обрамлять скобками разрешающими и запрещающими канселляцию.
> 
> А функцию очистки можно и в С++ юзать, проблем нету.
> Кроме того, если делать глобальный контроллер ресурсов, то лучше юзать pthread_key и pthread_get/setspecific. Там автоматом все очищается, своего рода общий деструктор потока.
> ...


 exception тут не причем. 
вы все пытаетесь тут всучить мне набор функций pthread_ХХХ 
и выдать это за средства самого Ц.  
Во-первых, все те же pthread_ХХХ я могу использовать из ЦПП, ничего не потеряв.
Во-вторых, pthread_ХХХ - это еще не весь мир. Я, например, их в галаза не видел - не работаю я на линухе/юнихе. В вин32 совсем другой набор 
потоковых ф-й.    
Вышеописанный код на ЦПП имеет только одно ограничение - все объекты нужно создавать в хипе через new и оборачивать в DisposeablePtr - все это исходит из предположения, что после убиения потока у него уже нет стека - поэтому все что он насоздавал должно быть в хипе. Ни на какие вспомогательные средства реализации подсистемы потоков он не опирается.
А вообще, какое дело имеет многопоточное программирование (что в больше части методология) к окончательной и полной победе Ц над ЦПП?

----------


## homo ludens

> я уверен, что такой подход ущербный, а если будет 1000 клиентов, а если больше?


 модель трид на клиента, когда поток создается при появлении клиента и удаляется при его уходе я никому не посоветую. В первую очередь потому, что сам имел глупость когда-то ей пользоваться.  

Но модель трид на клиент с использованием пула потоков в некоторых случаях вполне жизнеспособна. Зависит от синхронности базовых операций. Если есть что-то типа прокси или web-сервера, где вызовы клиента ждут асинхронного завершения файловых функций с сокетом или файлом, то "трид на клиент" не нужен никаким образом. А вот если каждый клиент постоянно чешет базу данных (и не внешний SQL а вкомпиленный DB manager), то тут вполне нормально - предел базы наступит раньше чем предел потоков. Кроме того есть плюс по безопасности, я могу привязать секюрити контекст к номеру потока после авторизации и запретить некоторые операции с другим номером потока.  Плюс подвисание клиентского потока не мешает остальным клиентам, а пострадавший просто переконнектится.
Системы в 600 потоков у меня работали в реальной жизни на одноядерных машинах. Как, это другой вопрос, но у меня допустимый latency - 400мс, пока хватало. И есть подозрение, что до 1000 потоков вполне дотянуло бы.
Кроме того ACE - "тяжелая" библиотека и кушать ресурс должна намного больше, чем pthread.




> Началось как бы с того что пимпл перед воид имет одно преимущество - отсутствие воид.


 И один недостаток - если добавляются виртуалки  возникает  перекомпиляция. А в С ее не будет никогда - это гарантированная пуленепробиваемая изочяция. Кроме того воид легко обойти - вместо typedef void* mytype_t пишешь typedef struct { void* data} mytype_t и компилятор прекращает приводить типы.  А в С++ без виртуалок прожить сложно - это слишком удобные и теплые тапочки, чтобы потом переобуваться в сапоги. Наследование интерфейсов без них красиво не организуешь.




> а вот почему из ц++ ушел в с, непонятно, эдак можно следующим языком ассемблер выбрать.


 Что-то не работает - спускаюсь на уровень ниже и пытаюсь разобраться. Так и дошел до жизни такой. ;-)
Кроме того, при моем подходе с предварительным построением макета С++ дает очень мало преимуществ и практически все они представляют собой синтаксический сахар, на который я обычно обращаю мало внимания.
В ассембелер не уйду - переносимость слабая плюс модели процов меняются. А С код остается с тобой на всю жизнь.
С С++ кстати та же проблема - это динамично развивающийся язык, который не всегда обращает внимание на обратную совместимость. И есть шанс, что написанные один раз много лет назад библиотеки придется править или включать специальный режим компиляции. У меня в одном древнем куске кода висит например инициализация массива с явными параметрами конструктора. Когда-то это было нормально, сейчас это не откомпилится никак.
Или вот пример реального кода (уже приводил в другом обсуждении):


```
#if GCC_VERSION >= 40000
namespace xxx
{
  namespace yyy
  {
#endif  

template <> char* func<bar>::foo(int dump) { return zen(dump); }

#if GCC_VERSION >= 40000
  }
}
#endif
```

 


> вы все пытаетесь тут всучить мне набор функций pthread_ХХХ 
> и выдать это за средства самого Ц.


 Это не средства С, это средства его стандартных библиотек. 



> Во-вторых, pthread_ХХХ - это еще не весь мир. Я, например, их в галаза не видел - не работаю я на линухе/юнихе. В вин32 совсем другой набор 
> потоковых ф-й.


 Вы ошиблись. Это практически весь мир. Это Portable Operating System Interface. И в винде POSIX threads тоже есть, включая СЕ. Пользуясь POSIX мой код будет портируем практически везде, включая Windows.
Разумеется для Win-специфичного софта разумней пользоваться его собственными потоками. А вот если надо делать платформенно независимое низкоуровневое решение - без pthreads не обойтись.




> Вышеописанный код на ЦПП имеет только одно ограничение


 Я пытаюсь показать почему такой код надо запретить к использованию по соображениям неправильного дизайна. 
1. создание объекта в хипе намного более затратно по сравнению с созданием объекта в стеке.
2.  однопоточный код и многопоточный начинают выглядеть полностью по разному.
3.  нелокальность кода. В моей реализации возникновение ожидания обрамляется прямо в коде вокруг ожидания. Ушло ожидание - ушел блок. Появилось - появился блок. Код правится только там, где произошло изменение. В твоей реализации - требуется переработка всего кода потока. Миграция функций из убиваемого потока в главный или наоборот приводит к полному переписыванию всего кода функции и всех вызываемых ей функций.  В проекте от 100к строк это просто нереально - ты будешь его постоянно перерабатывать, пока весь код не станет таким (кстати об уродливом коде). 




> А вообще, какое дело имеет многопоточное программирование (что в больше части методология) к окончательной и полной победе Ц над ЦПП?


 А я где-то писал, что С победит С++?
Я писал, что С++ придется уйти при смене глобальной парадигмы - фэнбои ООП слишком жестко привязали его к объектной технике и из универсального языка он превратился в объектный. А С - останется и мигрировать допустим в функциональную схему для него не составит проблем.
Глупо ожидать, что ООП будет жить вечно, не правда ли?

А многопоточное программирование, как и распределенные системы - это не будущее - это настоящее. У меня на конец года светится интересный заказ на гриде с MPI. И для многих программистов на С++ оказывается очень неприятным сюрпризом то, что в некоторых ситуациях объект может быть удален без выполнения деструктора. И чтобы уйти от этого неприятного факта - они пишут код, похожий на тот, который ты привел. Код который использовать нельзя ни в коем случае - это смерть проекта.

----------


## Guffy

> Вы ошиблись. Это практически весь мир. Это Portable Operating System Interface. И в винде POSIX threads тоже есть, включая СЕ. Пользуясь POSIX мой код будет портируем практически везде, включая Windows.
> Разумеется для Win-специфичного софта разумней пользоваться его собственными потоками. А вот если надо делать платформенно независимое низкоуровневое решение - без pthreads не обойтись.


 Нет, уважаемый. Имхо, это Вы ошиблись. В винде позикс-подсистема есть, да. Но использование двух подсистем в одной проге, насколько я помню, невозможно. Т.е. хотите pthreads от мелкософт - сидите в рамках позикса, а на функции Вин32АПИ можете только облизываться. И наоборот. Или нужны посторонние приблуды. Поэтому все опенсорс кросплатформенные проекты, которые я видел, пользуются своим слоем абстракции и на винде привязывают его к Вин32АПИ. 




> Я пытаюсь показать почему такой код надо запретить к использованию по соображениям неправильного дизайна. 
> 1. создание объекта в хипе намного более затратно по сравнению с созданием объекта в стеке.
> 2.  однопоточный код и многопоточный начинают выглядеть полностью по разному.
> 3.  нелокальность кода. В моей реализации возникновение ожидания обрамляется прямо в коде вокруг ожидания. Ушло ожидание - ушел блок. Появилось - появился блок. Код правится только там, где произошло изменение. В твоей реализации - требуется переработка всего кода потока. Миграция функций из убиваемого потока в главный или наоборот приводит к полному переписыванию всего кода функции и всех вызываемых ей функций.  В проекте от 100к строк это просто нереально - ты будешь его постоянно перерабатывать, пока весь код не станет таким (кстати об уродливом коде).


 А я пытаюсь объяснить, что такой дизайн был исходя из отсутствия привязки к конкретной модели тридов и предположения о худшем - после смерти потока памяти его стека уже нет. Но если в реальной жизни какая-то платформа даст мне поблажки (ну те же pthreads) - можете не боятся я, при желании, неприменно ими воспользуюсь в полный рост, еще и со всеми прелестями ЦПП.  И код будет как лялечка.
И оформлю все это дело так, что неважно сколько будет размер первоначального кода - главное, чтобы в нескольких (десятках, сотнях) тридпроц нужно было писать как можно меньше и меньше задумываться. 
Я еще и очень ленивый
"Лучше день потратить, потом за полчаса долететь".
Кстати, и еще неизвестно, что есть "плохой дизайн". Может это все таки этот замечательный пример, когда подсекают ноги синхронному вызову и неизвестно что это за собой потянет?   Да, это реалии, но все же... В винде, вроде бы, только вот к висте они пришли не просто к асинхронному ИО, но и с возможностью отмены ожидания. Может и разработчики разных БД когда-нибудь возьмутся за голову наконец-то.




> А я где-то писал, что С победит С++?
> Я писал, что С++ придется уйти при смене глобальной парадигмы - фэнбои ООП слишком жестко привязали его к объектной технике и из универсального языка он превратился в объектный. А С - останется и мигрировать допустим в функциональную схему для него не составит проблем.
> Глупо ожидать, что ООП будет жить вечно, не правда ли?
> 
> А многопоточное программирование, как и распределенные системы - это не будущее - это настоящее. У меня на конец года светится интересный заказ на гриде с MPI. И для многих программистов на С++ оказывается очень неприятным сюрпризом то, что в некоторых ситуациях объект может быть удален без выполнения деструктора. И чтобы уйти от этого неприятного факта - они пишут код, похожий на тот, который ты привел. Код который использовать нельзя ни в коем случае - это смерть проекта.


 Может конкретно этими словами и не писали, но эта лебединая песня красной линией проходит по ветке.  "С - останется и мигрировать допустим в функциональную схему" - лошади приделают крылья  
на мое имхо - массовые языки широкого универсального (или почти универсального) применения, это
C, C++, Object Pascal (Delphi), C# (ну и другие из семейства дотнета, ну да бог с ними), Java. Все остальное живет в своих прочно занятых нишах и нишках. И как кто ни пытался - эще из своей нишки не выбрался на большую дорогу. (про перлы, питоны, лу, бу и т.п. лучше не дразните - не знаю, и знать не хочу. да и не мейнстрим все это). А Страуструп был явно не дурачок, если C++ смог так подняться.
Все из этого списка, кроме жавы, я очень близко знаю. (могу сделать краткий сравнительный обзор) ну разве что на Ц реальных проектов не писал. и не буду. "нехочу" (с) проф.Преображенский.
Так вот, Object Pascal (Delphi), C# и Java - это, по большому счету, родственники идеологии C++. Каждый из них имеет некие уникальные фишки. Основные, это (ну кроме сборки мусора - это для некоторых святое) ссылки (как #include) на откомпилированные модули и более резвитая поддержка метаданных компилятором (проперти, рефлекшн). На рефлекшн C++ крыть практически не чем, но в остальном он по возможностям покруче будет, а остальные, соотвественно, по идеологии попроще. Но все это - ООП, как ни крути.
И ООП еще будет здравствовать, не так уж он болен, как вы считаете. И фенбои С делают ООП на С :P

Многопоточнось. Во многих прогах ее нет, не будет и она там не нужна. Там где она нужна, еще какую-то часть отобьют упрощенные модели типа OpenMP. И только оставшиеся будут пользоваться всеми прелестями и граблями.
Для многих программистов многое оказывается неожиданностью. Давайте не будем ставиь в укор какому-либо языку то, что им пользуются люди, занимающиеся не своим делом. 
А код определяется постановкой задачи и ограничениями окружения. И как его делать или не делать применительно к потокам - см. выше. Не было бы в птридах уже готовой приблуды - еще низвестно каким извратом на С нужно было бы выкручиваться.

----------


## homo ludens

> Нет, уважаемый. Имхо, это Вы ошиблись. В винде позикс-подсистема есть, да. Но использование двух подсистем в одной проге, насколько я помню, невозможно. Т.е. хотите pthreads от мелкософт - сидите в рамках позикса, а на функции Вин32АПИ можете только облизываться. И наоборот. Или нужны посторонние приблуды. Поэтому все опенсорс кросплатформенные проекты, которые я видел, пользуются своим слоем абстракции и на винде привязывают его к Вин32АПИ.


 Cтранно, что мои POSIX либы перекомпилируются без проблем под Win32. Наверное потому что не пользуюсь MS компилятором...

А pthread от MS я не хочу - я всего лишь хочу писать код не заморачиваясь под какой ОС он будет работать. Для того стандарты и придуманы. И потому на Win32api не облизываюсь - нафиг он мне нужен. Кто рисует окошки в виндах - пользуется моей либой в виде COM-объекта и все, никаких проблем. Так что использование двух подсистем в одной проге возможно.
Под опенсоурс вы наверное имеете в виду средства для рисования окошек? : -)




> А я пытаюсь объяснить, что такой дизайн был исходя из отсутствия привязки к конкретной модели тридов и предположения о худшем - после смерти потока памяти его стека уже нет.


 Тут наверное специальная модель процессора нужна, С++ совместимая. : -) Как вы вообще представляете себе вызов деструктора автоматического объекта после смерти трида? Трид умер, а поток команд есть?



> Но если в реальной жизни какая-то платформа даст мне поблажки (ну те же pthreads) - можете не боятся я, при желании, неприменно ими воспользуюсь в полный рост, еще и со всеми прелестями ЦПП.  И код будет как лялечка.


 Эта проблема не разрешима в принципе, даже с дополнительной поддержкой ядра ОС. Деструктор должен выполняться в другом триде, а для автоматических переменных это невозможно. Нужна поддержка языка. А ее нет ни в С++ ни в С. Просто в С нет деструкторов, потому там эта проблема сводится лишь к вызову cleanup.



> Кстати, и еще неизвестно, что есть "плохой дизайн". Может это все таки этот замечательный пример, когда подсекают ноги синхронному вызову и неизвестно что это за собой потянет?   Да, это реалии, но все же... В винде, вроде бы, только вот к висте они пришли не просто к асинхронному ИО, но и с возможностью отмены ожидания. Может и разработчики разных БД когда-нибудь возьмутся за голову наконец-то.


 Хороший дизайн должен допускать малые изменения кода при малом изменении логики. Мой дизайн это дает - ваш нет. Подсечка неатомарного синхронного вызова простыми методами в принципе не разрешима - ни в С ни в С++. Однако в GNU libc (не портируемое решение) я могу динамически подменить malloc через malloc_hook. И это решает проблему на 99%, если делать подмену с обратной подменой в скобках, обрамляющих неатомарный вызов. Другой вопрос, что если не видишь исходного кода той библиотеки, которую юзаешь, то не знаешь только ли динамическая память там юзается - может быть например глобальный массив, а его так не почистишь. Правильная либа обычно ведет себя нормально.




> "С - останется и мигрировать допустим в функциональную схему" - лошади приделают крылья


 ну приделали же ему два плюсика для ООП. Значит и в будущем сделают так же. Опыт ведь уже есть.



> на мое имхо - массовые языки широкого универсального (или почти универсального) применения, это
> C, C++, Object Pascal (Delphi), C# (ну и другие из семейства дотнета, ну да бог с ними), Java. Все остальное живет в своих прочно занятых нишах и нишках. И как кто ни пытался - эще из своей нишки не выбрался на большую дорогу. (про перлы, питоны, лу, бу и т.п. лучше не дразните - не знаю, и знать не хочу. да и не мейнстрим все это). А Страуструп был явно не дурачок, если C++ смог так подняться.


 Форум, на котором мы общаемся не написан ни на одном из этих языков. Он написан на PHP. Для управления сервером используется perl. Apache, на котором это все запущено написан на С, как и perl и PHP. Работа админа на этом сервере - написание shell-скриптов, которые тоже не на джава. shell-интерпретатор написан на С. Емейл, который проходит через этот сервер обрабатывается почтовиком, который тоже написан не на Дельфи. Таких серверов по миру миллионы. Не хотите знать про это - ваше право. Но мне интересно что же такое мейнстрим в вашем понимании? Сотни негров-кодеров в кубиклах? Тема для разговора с коллегами о новом паттерне? Microsoft Journal и DrDobbs где на один абзац умной мысли приходится 5 страниц пропаганды частной технологии и 10 - рекламы?
Имхо есть вещи о которых много говорят, а есть вещи которые много работают. Почувствуйте разницу.




> И ООП еще будет здравствовать, не так уж он болен, как вы считаете. И фенбои С делают ООП на С :P


 А я не считаю его больным, просто я помню как тоже самое говорили про структурное программирование. 
Кстати reflection в надстройках C++ есть, в том же OpenC++. Причем не только для чтения, а и с возможностью добавления новых членов класса.
И сборка мусора в С есть - пакет GC, его даже GNU компилятор использует.




> Многопоточнось. Во многих прогах ее нет, не будет и она там не нужна. Там где она нужна, еще какую-то часть отобьют упрощенные модели типа OpenMP. И только оставшиеся будут пользоваться всеми прелестями и граблями.


 Тогда выкиньте свой мультикор и замените на одноядерку. МП нужна в любом сервере, в любых ресурсоемких вычислениях, блин даже P2P клиента на ней наверное удобней писать. 
Хотя если вы рисуете окошки на винде... Только это имхо не называется программированием.
http://www.joelonsoftware.com/items/2006/08/01.html - Can Your Programming Language Do This?
(там же есть на русском, но ссылка не копируется).




> Для многих программистов многое оказывается неожиданностью. Давайте не будем ставиь в укор какому-либо языку то, что им пользуются люди, занимающиеся не своим делом.


 Из предыдущих постов мне показалось, что это было неожиданностью для Вас.



> А код определяется постановкой задачи и ограничениями окружения. И как его делать или не делать применительно к потокам - см. выше. Не было бы в птридах уже готовой приблуды - еще низвестно каким извратом на С нужно было бы выкручиваться.


 POSIX - очень абстрактная спецификация и рассчитана как раз так, чтобы реализовать оптимальную комбинацию максимальной полноты и минимальных наворотов. Опять таки Unix way. Наворачивать туда работу с памятью имхо было бы как раз плохим дизайном.

----------


## Guffy

Вы, видимо, видите весь мир программ через амбразуру серверов приложений. И так пренебрежительно относитесь к "окошкам". В позиксе под виндой вы будете иметь потоки, сокеты и файлы (включая потоки стандартного ввода и вывода). И все. Остальное - трики.
Чтоб вы не боялись за меня, что я любитель только "окошек", и что это вообще не программирование - начальство подшучивает надо мной, что я "двигателист". Они вечно поручали мне какие-то engine.

OpenC++, возможно - к сожалению, помер в конце 2004го. Т.е. с тех пор релизов не было и незвестно - будут ли.

На чем написан форум - меня абсолютно не трогает. С таким же успехом он мог бы быть на С#+ASP.NET+IIS. Или на С++ и ClearSilver. И почему в отдельной теме веб-программирования многие веб-программисты (видимо, очень уважаемые люди, в отличие от негров-кодеров в кубиклах) выбрали ПХП - меня тоже не чешет. 
И пугать меня почтовыми серверами не стоит.  Может и на дельфи есть такие. А то я вас буду пугать сотнями миллионов пользователей Windows Media Player, который, вполне не исключено (но, возможно и нет), сделан на С++. А также другими штучками, которые можно встретить на каждом десктопе (и линуховом в том числе). А десктопов поболее будет, чем серверов.  Короче, бухгалтерию тут разводить ни к чему.

И чтоб уже подвязать с мультипоточностью, повторюсь:
1. Если мне нужен будет позикс для мультипоточности - я его возьму и не поперхнусь. и в ЦПП в том числе.
2. То, что остаток кода не выполняется при остановке потока, и дестуркторы в том числе - для меня не новость. Тут вы не угадали.
3. Вышепреведенный пример на это был рассчитан с самого начала. Когда делается delete DisposeableThread - там все подчищается.
4. Если я буду уверен, что после чего-то типа thread_terminate и переред чем-то типа thread_delete будет еще жива память, которая была стеком потока и если воспользоваться __thread - код можно упростить и я смогу даже вызывать деструкторы автоматических переменных, наследованных от определенного класса. могу попробовать доказать.
5. Вы, как любитель во всем разбираться внутри, возможно *внутрь* птридов не очень то и заглядывали. Вполне вероятно, что там есть нечто подобное, а то и похлеще. Вы просто выучили этот АПИ, приноровились к нему и пользуетесь. Если это Вас полностью устраивает - я Вас вполне без шуток поздравляю. Просто не надо сравнивать то, что вам дают готовое, с тем, что вам бы пришлось делать самому, если б вам не дали ничего.

----------


## homo ludens

> Вы, видимо, видите весь мир программ через амбразуру серверов приложений. И так пренебрежительно относитесь к "окошкам". В позиксе под виндой вы будете иметь потоки, сокеты и файлы (включая потоки стандартного ввода и вывода). И все. Остальное - трики.


 Не к окошкам, а к регулярно подвисающим приложениям. Я не понимаю, почему программист - человек с высшим образованием и неплохим интеллектом должен рисовать окошки вместо человека предназначенного для этого - дизайнера. И я не понимаю, как программисты ценящие свое время и свой интеллект до сих пор не написали чего-то, что способно автоматизировать сей глупый труд, вместо того, чтобы ждать новых откровений от MS.

Поймите, я не использую ресурсы ОС, я пишу код, который должен выпоняться везде. И если я напишу 
int x =99999; char *y = (char*)&x; char z= *y; то это сработает на x86 и не сработает на Sparc.
И если я напишу printf("%d",sizeof(x)), то это пройдет в 32-х битном интеле и вызовет креш в 64-битном АМД.
Нельзя мне юзать платформенно-зависимые вещи.




> OpenC++, возможно - к сожалению, помер в конце 2004го. Т.е. с тех пор релизов не было и незвестно - будут ли.


 И чем это мешает им пользоваться? 




> И пугать меня почтовыми серверами не стоит.  Может и на дельфи есть такие. А то я вас буду пугать сотнями миллионов пользователей Windows Media Player, который, вполне не исключено (но, возможно и нет), сделан на С++. А также другими штучками, которые можно встретить на каждом десктопе (и линуховом в том числе). А десктопов поболее будет, чем серверов.  Короче, бухгалтерию тут разводить ни к чему.


 Количество пользователей почтовых серверов больше количества десктопов - здесь и роботы и автомейлеры и мобильщики и т.п. И если бы почтовый сервер как Windows Media Player говорил что не может найти кодек для чарсета или отсылал бы почту на другой адрес - инета бы не было. Надежность и качество приложений разное. И время жизни тоже. Storage leak в десктопе неважен - прикладуха не работает месяцами и годами, так что можно терять память сколько угодно.
Кстати, не знаю ни одного человека, который бы юзал WMP - даже у моей знакомой блондинки стоит ZoomPlayer + Winamp. Откуда такая цифра - 100М?




> 5. Вы, как любитель во всем разбираться внутри, возможно *внутрь* птридов не очень то и заглядывали. Вполне вероятно, что там есть нечто подобное, а то и похлеще. Вы просто выучили этот АПИ, приноровились к нему и пользуетесь. Если это Вас полностью устраивает - я Вас вполне без шуток поздравляю. Просто не надо сравнивать то, что вам дают готовое, с тем, что вам бы пришлось делать самому, если б вам не дали ничего.


 Блин, как можно заглянуть внутрь *спецификации*? POSIX.1c - это набор документов, куда там заглядывать? 
В реализацию на Линухе я заглядывал давно, когда были linuxthreads, сейчас время NPTL, а допустим в реализацию AIX не заглядывал, не было у меня в тот момент AIX, да и сейчас нет. В HPUX версии которая у меня была - вообще никаких тридов не было, ни позиксов, ни каких-то других.
За дискуссией по поводу linuxthreads - NPTL - NGPT давно следил, уменьшение времени запуска 100000 потоков с 15 минут до 2 секунд меня например порадовало, отлаживаю софт я на линухе, стресс тесты пошли быстрее.
Только для портируемого приложения это все бесполезно - не буду я юзать футексы вместо мутексов, потому что тогда ни винда ни AIX меня не поймут. И я не знаю сколько потоков поднимется на винде или HPUX за 2 секунды, потому экономлю каждый.

А насчет готового АПИ, вы правы, чего я еще неписал в этой жизни - это sheduler. ;-) От этого бог миловал. Разве что на спор в 80-х на синклере в учебных целях (там прерывание таймера 50 ли 25  гц уже не помню переключало контексты  - такая вот учебная задачка. Но назвать это планировщиком конечно трудно.

----------


## Guffy

> Не к окошкам, а к регулярно подвисающим приложениям. Я не понимаю, почему программист - человек с высшим образованием и неплохим интеллектом должен рисовать окошки вместо человека предназначенного для этого - дизайнера. И я не понимаю, как программисты ценящие свое время и свой интеллект до сих пор не написали чего-то, что способно автоматизировать сей глупый труд, вместо того, чтобы ждать новых откровений от MS.
> 
> Поймите, я не использую ресурсы ОС, я пишу код, который должен выпоняться везде. И если я напишу 
> int x =99999; char *y = (char*)&x; char z= *y; то это сработает на x86 и не сработает на Sparc.
> И если я напишу printf("%d",sizeof(x)), то это пройдет в 32-х битном интеле и вызовет креш в 64-битном АМД.
> Нельзя мне юзать платформенно-зависимые вещи.


 Во первых, непонятно, какая связь между окошками и регулярно подвисающими приложениями. Это опять же ортогональные вещи.
Во вторых, по поводу отделения дизайна окошек от логики - ту, вы может 
будете удивлены, но подвижки уже есть. И сделал это, в реальной жизни, а не в концепции, как ни странно, мелкософт. WPF, XAML - новейшие фишки 3го фреймворка (я, правда, пока толком не ознакомился, но хочу).
А вообще _жисть_ штука сложная. И я больше сталкивался не с тем, что нужно написать высокоэлегантный кроссплатформенный код и чтобы задача нравилась. Нужно написать конкретный проект на конкретной платформе и в конкретные (сжатые) сроки. И еще по уродской спецификации. И ежики плачут, плюются, но лезут на кактус.




> И чем это мешает им пользоваться?


 Ничего. Кроме того, что если на момент помирания там остались куски, приделанные на коленке, то доделывать уже только самому, разгребая до кончиков костей. Да и просто странно. Либо автор утратил интерес к теме сам по себе. Либо столкнулся с чем-то, что оказалось проще плюнуть и забросить.
Кстати, вообще-то эта кухня что-то вроде "препроцессора" к ЦПП, так что в достоинства самого ЦПП рефлекшн я пока торопиться добавлять не буду 





> Количество пользователей почтовых серверов больше количества десктопов - здесь и роботы и автомейлеры и мобильщики и т.п. И если бы почтовый сервер как Windows Media Player говорил что не может найти кодек для чарсета или отсылал бы почту на другой адрес - инета бы не было. Надежность и качество приложений разное. И время жизни тоже. Storage leak в десктопе неважен - прикладуха не работает месяцами и годами, так что можно терять память сколько угодно.
> Кстати, не знаю ни одного человека, который бы юзал WMP - даже у моей знакомой блондинки стоит ZoomPlayer + Winamp. Откуда такая цифра - 100М?


 Ну давайте тогда еще по косточкам разберем, кто пользуется стеком TCP\IP  
И вспомним легендарные истории о багах в sendmail. И какое шаманство было заполнить sendmail.conf. И про "открытые релеи" вспомним. Надежность почты сейчас - это тысячи битых граблями голов на протяжении полутора-двух десятков лет. 
И вы будете тыкать пальцем, но я пользую WMP 11. Так же как и некотрое другие плейеры. И VLC пользую, у которого масса уникальных фишек, но который если как минимум раз 10 не рухнет за день - у него день зря прошел (кстати, ООП на Ц - чистое здоровье  ). 
А WMP падал очень редко, на откровенно битых файлах, на которых и другие ноги ламают.
А еще на ЦПП мозилка и КДЕ со всей своей гопкомпанией

----------


## homo ludens

> Во первых, непонятно, какая связь между окошками и регулярно подвисающими приложениями. Это опять же ортогональные вещи.


 Ох, если бы так... Бизнес модель быстрой разработки и короткий жизненный цикл программ провоцируют на постоянные глюки. Плюс неквалифицированный конечный пользователь, которому стараются сделать приятно всеми способами.




> Во вторых, по поводу отделения дизайна окошек от логики - ту, вы может 
> будете удивлены, но подвижки уже есть. И сделал это, в реальной жизни, а не в концепции, как ни странно, мелкософт. WPF, XAML - новейшие фишки 3го фреймворка (я, правда, пока толком не ознакомился, но хочу).


 Сильно интересовался XUL от Мозиллы, но имхо до удобства использования там далековато. 
Вообще в прошлом году написал для себя требования к GUI библиотеке, с которой мне лично было бы удобно работать. Пока ничего похожего не нашел, ни в дотнете ни где-то еще.
Но правда дотнет3 пока еще не смотрел, жду пока все желающие фэнбои понаступают на грабли и покричат об этих граблях в сети. А попозже, по расчищенному месту и идти приятней... 




> А вообще _жисть_ штука сложная. И я больше сталкивался не с тем, что нужно написать высокоэлегантный кроссплатформенный код и чтобы задача нравилась. Нужно написать конкретный проект на конкретной платформе и в конкретные (сжатые) сроки. И еще по уродской спецификации. И ежики плачут, плюются, но лезут на кактус.


 Бизнес-модель такая. Вот например, вы никогда не задумывались над тем, почему среда разработки от МС *всегда* будет супер интегрированная с кучей собственных фич и если бы у них была возможность продавать или давать на шару в комплекте с IDE кресло для программиста с автоподогревом - они бы занялись этим сразу? А вот тот же GNU - компонентнтая система допускающая использование любых библиотек в своем коде и основанная на минимализме, и IDE в мире GNU намного менее популярны. С чего бы это? Почему МС *никогда* не сделает GUI-библиотеки кроссплатформенными понятно, но почему МС, известная тем, что продвигала множество стандартов в разных областях (например протокол DHCP - стандарт МС) никогда не стандартизирует GUI?
Я вот например ответ знаю.




> Ничего. Кроме того, что если на момент помирания там остались куски, приделанные на коленке, то доделывать уже только самому, разгребая до кончиков костей. Да и просто странно. Либо автор утратил интерес к теме сам по себе. Либо столкнулся с чем-то, что оказалось проще плюнуть и забросить.
> Кстати, вообще-то эта кухня что-то вроде "препроцессора" к ЦПП, так что в достоинства самого ЦПП рефлекшн я пока торопиться добавлять не буду


 Несколько лет назад делал на нем всякие мелочи, потом попробовал реализовать контрактный инвариант класса и понял, что проще написать собственный кодогенератор. Не то что бы это не работало, но писать препроцессорный код на С++ - это все равно что писать шелл-скрипт на джаве. 
А потом представил как это бы выглядело на С и понял, что это надо срочно реализовывать. 




> И вспомним легендарные истории о багах в sendmail. И какое шаманство было заполнить sendmail.conf. И про "открытые релеи" вспомним. Надежность почты сейчас - это тысячи битых граблями голов на протяжении полутора-двух десятков лет.


 сендмейл это еще цветочки, а вот telnetd, который из BSD перекочевал куда только мог дотянутся (включая опорные маршрутизаторы Cisco) - вот это была бомба, которая похоже продолжает взрываться в разных местах. Последний раз в Cisco-роутерах года 4 назад бахнула.
Однако весь этот код (сендмейл и телнетд) - это унаследованный код раннего периода взрывного роста интернета. Сейчас так не пишут как тогда - с использованием небезопасных функций, без проверки на переполнение буфера, без жестких тестов и т.п..




> А еще на ЦПП мозилка и КДЕ со всей своей гопкомпанией


 Дык я всегда говорил, что кроме ГУИ мне С++ не дает практически никакой выгоды.
ООП, реализованное на С по глупому - это попытка сделать кальку с С++ реализаций с помощью препроцессора. Сначала стандартного, потом самописного. Имхо это тяжелый бред, потому что если хочешь из С сделать С++, то тогда почему не пользоваться С++ сразу? Нужно такси или нужны шашечки?
А вот реализация например инкапсуляции на С его же средствами происходит совсем по другому, чем на С++ и иногда выходит даже четче чем на С++ - см. мою дискуссию с Ull9.

----------


## Phoenixxe

> Про ACE слышал очень много хорошего, однако сам с ним не успел поработать. С другой стороны в годы увлечения CORBA пытался запустить TAO (их смежный продукт) - увы, мне не удалось его даже скомпилировать. Сейчас наверное ситуация изменилась к лучшему, но тогда (2002 +-год) это был продукт выглядевший очень сыровато.


 в 2006 скомпилировался в Visaul Studio 2005
all ok

----------

