LINUX.ORG.RU

Несколько вопросов: языки высокого уровня и С++ с точки зрения программистов на С (kernel, embedded, realtime, ...)


0

0

Если бы вы изобретали С++ с чистого листа, то как бы вы...?

___

Чтобы тема была чуть определеннее, приведу несколько обычных притензий к С++, и мои предложения как это исправить:

1. Вроде-как-объявление переменной Something s=1; может вызвать сколь угодно длинное вычисление

Решение: разный синтаксис:

Something s=1; означает, что конструктор s завершается за заранее известное число таков (и деструктора нет?)

Something s(1); означает... что ожидать можно что угодно.

2. С++ поддерживает перегрузку оператора [] -- и это значит, что a["asdf"] может обращаться за значением к серверу на другой стороне Земли, не говоря уж о произвольном таймауте и возможном выбросе исключения. В то же время абстрактная запись типа a["asdf"] для языка высокого уровня *НУЖНА*. Поэтомо, возможно, следует *на уровне синтаксиса* различать абстрактное и гарантированно-себя-хорошо-ведущее.

Решение: разный синтаксис:

a.[i] означает, что компилятор гарантирует, что i не выйдет за пределы границ массива а, операция завершается за заранее известное число таков, и проверка в ран-тайме *не нужна*, как например в случае for(int i=0; i<sizeof(a)/sizeof(a.[0]); i++) do_something(a.[i]);

a.at(i) (или a.at[i]?) означает, что проверка диапазона (или наличия значения в хэше, или ... ) производится, операция завершается за заранее известное число тактов; выход за границы -- это критическая ошибка логики программы, и при ее наличии программа должна gracefully завершиться.

a[i] означает... что ожидать можно что угодно.

3. Исключительно тормозные плюсовые исключения (на десятичные порядки) http://www.linux.org.ru/view-message.jsp?msgid=3856841

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

(Понятно, что все это требует более умного компилятора)

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

Ответ на: комментарий от LamerOk

> Exception для учёта/обработки _ожидаемого_ т.е. штатного события - моветон.

0.1% синтаксически поломанных строк -- это ожидаемое штатное событие или нет? и когда оно становится нештатным?

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от LamerOk

> wlor, в этом треде ты просто идеальный образчик самых типовых exception abuse.

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

1. быдлокодер должен иметь право на exception abuse

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

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

> 0.1% синтаксически поломанных строк -- это ожидаемое штатное событие или нет? и когда оно становится нештатным?

Процент указан абсолютно бессмысленно. Либо семантически (в рамках предметной области) "поломанные строки" вообще не допустимы и появление хотя бы одной - выброс исключения, либо они допустимы в любых количествах и не являются преметом для выброса исключений вообще.

LamerOk ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> быдлокодер должен иметь право на exception abuse

А почему только на exception abuse? Может еще на иногрирование стиля и обфускацию кода?

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


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

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

> Процент указан абсолютно бессмысленно. Либо семантически (в рамках предметной области) "поломанные строки" вообще не допустимы и появление хотя бы одной - выброс исключения, либо они допустимы в любых количествах и не являются преметом для выброса исключений вообще.

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

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от LamerOk

> А почему только на exception abuse? Может еще на иногрирование стиля и обфускацию кода?

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

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

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

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

)))

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

> Кстати, ты сам начал этот тред, с плача о плохой читаемости плюсов. )))

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

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

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

Наоборот. В блоке catch {} любой нормальный программист ожидает увидеть логирование события и либо код очистки, либо выброс нового исключения на уровень вврех. Что-либо иное - разрыв шаблона и необходимость вкуривать в запутанную логику и соотнесения потока выполнений исключений с нормальным потоком выполнений. Сама необходимость такого соотнесения - громадный минус читаемости. Чтобы понять твой код, мне пришлось разобрать его строчку за строчкой. Честный if и говорящие названия функций check_syntax() и calculate_traffic() (вместо бессымсленного traffic()) были бы понятны с первого взгляда.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

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

Мы считаем трафик и количество поломанных строк, естественно мы выведем юзеру оба числа -- трафик на stdout, количество поломанных строк -- в лог и/или на stderr, пусть юзер решает -- не слишком ли их много, чтобы доверять трафику.

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от LamerOk

> В блоке catch {} любой нормальный программист ожидает увидеть логирование события и либо код очистки, либо выброс нового исключения на уровень вврех.

Да, там именно логируется событие, в достаточно примитивной форме -- в форме его подсчета.

Можно это переписать -- допустим, непостредственно выплевывать первые 10 поломанных строк на stderr, а потом выплевывать число.

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от LamerOk

Да, блин, вот же:

int amount=0, invalid=0;
while( line=get_line() )
{
  try{
    amount+=traffic(line);
  }catch(....){
    invalid++;
  }
}

и

int amount=0, invalid=0;
while( line=get_line() )
{
  if (check_syntax(line))
    amount+=calculate_traffic(line);
  else
    invalid++;
}
LamerOk ★★★★★
()

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

int amount=0, invalid=0;
while( line=get_line() )
{
  try{
    amount+=traffic(line);
  }catch(InvalidLine){
    if(++invalid<10)
      std::cerr << "invalid line: " << line;
  }
}
if( invalid )
  std::cerr << "invalid line count: " << invalid << "\n";

std::cout << "traffic: " << traffic << "\n"
www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от LamerOk

> if (check_syntax(line)) > amount+=calculate_traffic(line);

check_syntax(line) и calculate_traffic(line) делают ровно одну и ту же работу

изучи, зачем появились исключения

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

- std::cout << "traffic: " << traffic << "\n"
+ std::cout << "traffic: " << amount << "\n"

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

> check_syntax(line) и calculate_traffic(line) делают ровно одну и ту же работу

Тут я бессилен. Либо человек умеет программировать, либо не.

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


Давай бартер - ты изучишь, как их использовать )))

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

>Exception для учёта/обработки _ожидаемого_ т.е. штатного события - моветон.

А чем checked exception это не штатное событие? Это как возвращаемый результат для редких но ожидаемых неудачных сценариев работы функции типа перегруженности удаленного сервера или зажеванной строчки в лог-файле. Для нештатных событий типа разрушения инвариантов или хардверного отказа RuntimeException предназначен.

Absurd ★★★
()
Ответ на: комментарий от www_linux_org_ru

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


LamerOk ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

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

Тоже самое говно, вид сбоку. Опять таки сравни, заодно снимем липовое возражение «check_syntax(line) и calculate_traffic(line) делают ровно одну и ту же работу».

int amount=0, invalid=0;
while( line=get_line() )
{
  try{
    amount+=traffic(line);
  }catch(InvalidLine){
    if(++invalid<10)
      std::cerr << "invalid line: " << line;
  }
}
if( invalid )
  std::cerr << "invalid line count: " << invalid << "\n";

std::cout << "traffic: " << amount << "\n"
int amount=0, invalid=0;
while( line=get_line() )
{
  char *s;
  if (s = get_traffic_szptr(line))
    amount+=std::atoi(s);
  else
    if(++invalid<10)
      std::cerr << "invalid line: " << line;
}
if( invalid )
  std::cerr << "invalid line count: " << invalid << "\n";

std::cout << "traffic: " << amount << "\n"
LamerOk ★★★★★
()
Ответ на: комментарий от Absurd

> А чем checked exception это не штатное событие?

Своей нештатностью. checked exception - это не штатное, но _предсказуемое_ событие. Иными словами, вероятный отказ.

> Для нештатных событий типа разрушения инвариантов или хардверного отказа RuntimeException предназначен.


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

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


Всё верно! И в коде catch {} я ожидаю увидеть повтор попытки записи или откат всей процедуры. Но я совсем не ожидаю увидеть там перерасчет записываемых данных.

LamerOk ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

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

ott ★★★★★
()
Ответ на: комментарий от LamerOk

Опять таки сравни, заодно снимем липовое возражение «check_syntax(line) и calculate_traffic(line) делают ровно одну и ту же работу».

if (s = get_traffic_szptr(line)) amount+=std::atoi(s);

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

int main() {
   ...
   if( g(....)==OTHER_FAILURE )
     ...
   else
     ...
}

OtherReturnCode g(....) { 
  if ( g(....)==FAILURE )
    return OTHER_FAILURE;
  else
    ...
}

SomeReturnCode f(....) { 
  if (s = get_traffic_szptr(line))
    amount+=std::atoi(s);
  else
    return FAILURE;
}

в то время как вариант с исключениями годится для *обоих* случаев:

int main() {
   ...
   try{
     cout << g(....);
   }catch(InvalidLine){
     ...
   }
}

doubl g(....) { 
  int i=1+f(....);
}

int f(....) { 
    amount+=std::atoi(s);
}
www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru
int f(....) { 
    amount+=std::atoi(get_traffic_szptr_with_exception(line)); // FIXED
}
www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

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

Разумеется. В твоём коде этого не было. Был выбран другой вариант - игнорировать.

> в то время как вариант с исключениями годится для *обоих* случаев:


1. Ты привел вообще левый код, не имеющий отношения к первому.
2. Стратегию обработки ошибок нужно выбирать еще на этапе проектирования и в явном виде заносить в TODO/ТЗ без всяких двусмысленностей.

LamerOk ★★★★★
()
Ответ на: комментарий от ott

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

ну так желательно эти языки назвать

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от LamerOk

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

bool success;
uint64_t amount;
success, amount = get_amount_from_log_line(line);
if (success) {
  ....  
} else {
  ++invalid_lines;
}
Absurd ★★★
()
Ответ на: комментарий от kemm

>Синтаксис у него не очень удобный, имхо

Я бы тут сказал что дело тут не в удобстве, а в том что это "стероидная" фича. ИМХО по уму надо такие вещи протаскивать в язык с самого низа, от бинарного протокола вызова и линкера.

Absurd ★★★
()
Ответ на: комментарий от Absurd

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

Тут можно заюзать не таплы, а Алг.ТД, Maybe<int> (а ламерок предложил фактически Maybe<char*>), и обычно Maybe<int> изображается как int*, но тогда возникает геморрой с освобождением памяти.

[code] while( line=getline()) { Maybe<int> t=traffic(line); if( t==NOTHING ) invalid++; else amount+=t.value; }

while( line=getline()) { int* t=traffic(line); if( t==NULL ) invalid++; else{ amount+=*t; free(t); } } [/code]

но возражения те же, что я сказал ламерку -- теряется гибкость

(впрочем, есть и положительные стороны АлгТД -- в с++ невозможно записать в переменную в виде АлгТД возвращенное значение *вместе с исключением*, а не кидать исключение)

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от Absurd

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

Тут можно заюзать не таплы, а Алг.ТД, Maybe<int> (а ламерок предложил фактически Maybe<char*>), и обычно Maybe<int> изображается как int*, но тогда возникает геморрой с освобождением памяти.

 
while( line=getline()) 
{ 
  Maybe<int> t=traffic(line);
  if( t==NOTHING )
    invalid++; 
  else
    amount+=t.value;
}

while( line=getline()) 
{ 
  int* t=traffic(line); 
  if( t==NULL ) 
    invalid++; 
  else{
    amount+=*t;
    free(t);
  }
} 

но возражения те же, что я сказал ламерку — теряется гибкость

(впрочем, есть и положительные стороны АлгТД — в с++ невозможно записать в переменную в виде АлгТД возвращенное значение *вместе с исключением*, а не кидать исключение)

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

www_linux_org_ru ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.