Pull to refresh

Comments 165

UFO just landed and posted this here
Скоро будет еще одна, немного попроще. А потом еще ;)
Данная проблема описана у Страуструпа.
Объявление и определение встроенной ф-ции должны находится в одной области видимости.

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

«Очень просто: подставляемые методы нужно определять непосредственно в header-файле (не обязательно внутри объявления класса). При этом ошибки повторного определения не возникает, так как компилятор говорит компоновщику игнорировать ODR»
Тут небудет двойного определения, вы в объявлении класса объявите inline метод, а потом определите его. Все по чесному, никаких отключений проверок.

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

>Тут небудет двойного определения, вы в объявлении класса объявите inline метод, а потом определите его. Все по чесному, никаких отключений проверок.
Допустим, у нас есть хедер A.h, в котором объявлен класс A с inline-методом, определение которого находится в том же хедере.
Этот хедер мы инклудим в файлы B.cpp и C.cpp, которые представляют собой отдельные единицы трансляции и на стадии компиляции образовывают отдельные объектные файлы.
При сборке оба файла попадают в бинарник, а значит там будет двойное определение. Или я что-то неправильно понимаю?
По стандарту адрес будет в любом случае. А формат ф-ции(встроит ее компилятор или нет) будет зависеть от настроек этого самого компилятора. Вобще современные компиляторы и компоновщики дело темное и зачастую они игнорируют указания программиста.

Два определения приемлимы если:
1. Находятся в разных единицах трансляции.
2. Они одинаковы.
3. Значение лексем одинаково.

В случае А, В и С все три пункта сходятся, значит ODR выполняется.

> «По стандарту адрес будет в любом случае. А формат ф-ции(встроит ее компилятор или нет) будет зависеть от настроек этого самого компилятора. Вобще современные компиляторы и компоновщики дело темное и зачастую они игнорируют указания программиста».

Компилируем в MS Visual Studio c ключом /Ob1 (это Inline Function Expansion: Only __inline) в релизной конфигурации следующий код:
// A.h

#ifndef _A_H_
#define _A_H_

#include <iostream>

class A
{
public:
  inline void foo();
};

void A::foo()
{
  std::cout << "A::foo()" << std::endl;
}

// main.cpp

#include <cstdlib>
#include <iostream>
#include "A.h"

int main()
{
  A a;
  a.foo();
  return EXIT_SUCCESS;
}


Смотрим Disassembly:
int main()
{
  A a;
  a.foo();

00401000 mov ecx,dword ptr [__imp_stlp_std::cout (4020ACh)]
00401006 push esi
00401007 push offset string "A::foo()" (402114h)
0040100C mov esi,ecx
0040100E call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::_M_put_nowiden (4020B0h)]
00401014 push offset stlp_std::endl<char,stlp_std::char_traits<char> > (401030h)
00401019 mov ecx,esi
0040101B call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::operator<< (4020A4h)]


Inline-метод был подставлен на место вызова. Ни кода метода, ни вообще класса в бинарнике нет.

Отредактируем main.cpp:
// main.cpp

#include <cstdlib>
#include <iostream>
#include "A.h"

int main()
{
  A a;
  a.foo();

  void (A::*pF)() = &A::foo; // берем адрес подставляемого метода
  (a.*pF)(); // используем

  return EXIT_SUCCESS;
}


Снова смотрим Disassembly:
int main()
{
00401030 push ecx
  A a;
  a.foo();

00401031 mov ecx,dword ptr [__imp_stlp_std::cout (4020ACh)]
00401037 push esi
00401038 push offset string "A::foo()" (402114h)
0040103D mov esi,ecx
0040103F call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::_M_put_nowiden (4020B0h)]
00401045 push offset stlp_std::endl<char,stlp_std::char_traits<char> > (401060h)
0040104A mov ecx,esi
0040104C call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::operator<< (4020A4h)]


void (A::*pF)() = &A::foo;
  (a.*pF)();


00401052 lea ecx,[esp+7]
00401056 call A::foo (401000h)


В месте вызова метода по-прежнему была подстановка. НО! Там, где мы получаем адрес метода и используем его, стоит call. По этому адресу появилось вот что:

void A::foo()
{
  std::cout << "A::foo()" << std::endl;

00401000 mov ecx,dword ptr [__imp_stlp_std::cout (4020ACh)]
00401006 push esi
00401007 push offset string "A::foo()" (402114h)
0040100C mov esi,ecx
0040100E call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::_M_put_nowiden (4020B0h)]
00401014 push offset stlp_std::endl<char,stlp_std::char_traits<char> > (401060h)
00401019 mov ecx,esi
0040101B call dword ptr [__imp_stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::operator<< (4020A4h)]
00401021 pop esi

}

То есть, тело метода все-таки было сгенерировано отдельно!

> «Два определения приемлимы если…»
Но ведь определения все-таки два! ;) Сойдемся на том, что конфликта здесь не возникает.

UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Там это есть, 9.3/2:

A member function may be defined (8.4) in its class definition, in which case it is an inline member function (7.1.2), or it may be defined outside of its class definition if it has already been declared but not defined in its class definition.
По-моему скоро ситуация с популярностью программирования на C++ намного улучшиться из-за того, что Qt будет набирать популярность. Спасибо за статью. Продолжайте в том же духе
UFO just landed and posted this here
Java и без того больше некуда набирать популярность :)
До статей о программировании с использованием Qt я еще тоже доберусь ;) Спасибо за поддержку.
Заглловочные файлы и необходимость определния функций до ее использования — одна из неудобностей языка C/C++, какой-то дикий пережиток прошлого.
За что минус? В чём он неправ?
Он просто не имеет их готовить :) У каждого языка своя ниша и парадигма, нужно использовать их по назначению и при необходимости совмещать — тогда всё будет в шоколаде.
Я согласен, что можно описать вагон того полезного, что можно сделать при помощи хидеров. Но в повседневной практике они только мешают.
Я не против хидеров, только хотелось бы видеть их опциональными: встроенный #include в C# никому не помешает, лишь наоборот (орать «препроцессор зло» все горазды, но стандарт C++ сам же и говорит, что если тебе что-то не нужно, то не используй это, никто не заставляет).
А мне вот в Java препроцессора не хватает :-)
Нет, это однозначно недостаток.

1) необходимость писать вещи вроде

#if! defined PATH_TO_FILE
#define PATH_TO_FILE

это изврат. В других языках есть вещи вроде require_once(), намного удобнее

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

3) ну и дурацкиое правило, что функция должна быть определена, перед использованием — мне непонятно. Им что, лень добавить лишний проход компилятора, лучше пусть программист мучается?
Им что, лень добавить лишний проход компилятора, лучше пусть программист мучается?

Во-первых компиляция и так не летает, а во-вторых, сдаётся мне, существует пример, в котором без предварительного объявления выбрать верный путь разбора будет невозможно, хотя я его привести не возьмусь.
По поводу не летает — компилятор может создавать .h файлы, кешируя таким образом хедер без необходимости создавать повторно. Ну и использование предкомпилированных хедеров может еще ускорить этот процесс.

> в котором без предварительного объявления выбрать верный путь разбора будет невозможно

сильно сомневаюсь
1) #pragma once?

2) Генерировать это умеют и IDE. Хотя бы MSVS для MFC-проектов. А причём здесь компилятор?

3) Понимаете, программы на C/C++ бывают ОЧЕНЬ большие. Мой проект имеет исходников на 400Mb. И это далеко не предел, это так, «программулинка». Полный ребилд длится минут 15. Если сюда ещё добавить второй проход компилятора (просто потому, что программисту лениво располагать объявление функции до её использования) я повешусь :)
1) насколько я помню, #pragma once не стандартизирована…

2) согласен

3) Это правда. Мне приходилось сталкиваться с проектом, который минут 20-25 собирался распределенно на десятке не самых слабых компов.
#pragma once да, нестандартизована, тут не спорю. Я эту директиву упомянул лишь для того, чтобы показать, что C/C++ не такой тупой, древний и не развивающийся, как некоторым кажется. ;)

Хотя сам я делаю классически через #ifndef — у меня руки не отсохнут :)
1) я бы предпочеk #require_once, определять необходимость повторного инклюда в месте вызова
2) Что за ерунда? Я не хочу ставить IDE, что без GUI-приблудины уже и заголовки не сгенерировать? Писать код мышкоя я отнюдь не собирался.

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

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

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

Наверное, Вы себе слабо представляете, что означает действительно большой проект ;)
Попробуйте сгенерировать static версию Qt со static библиотекой MySQL и поддерживать всё это в актуальном состоянии.
Думаю без хедеров вы взвоете в скором времени :)
С++ сам по себе пережиток прошлого, чем скорее на нем перестанут писать — тем лучше. Для любой задачи он — не пришей кобыле хвост.
UFO just landed and posted this here
Зависит от задачи. Для системного программирования — Си, для массового прикладного под винду есть C# и жаба, для прикладного со сложной логикой можно писать eDSL на LISP с транслятором в подмножество Си и «склейкой» с помощью все того же Си.
UFO just landed and posted this here
Нет, он простой и удобный, в отличие от Си++
UFO just landed and posted this here
> Но он динамический, в отличии от С++, значит у него другая ниша.

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

> К тому-же ему 50 лет :-P

Современные языки только сейчас начинают получать фичи, которые были в лиспе десятки лет назад. Фактически, каждый новый мейнстримовый язык становится все больше похож на лисп.
UFO just landed and posted this here
Плохая идея, я обхожу строной программы на Java или .NET (из-за проблем с производительностью и памятью)
В приложениях, которые обычно пишутся на этих языках куда важнее скорость разработки, чем производительность.
Скажи, какого типа приложения пишешь?
Я вообще на PHP пишу (там тоже быстро))), а Java/.NET может и приемлемо работают, когда одна программа запущена, но у меня такого не бывает. И еще они заупскаются немгновено (а иногда очень немгновенно) и это неприемлемо.

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

А у java-программ дурацкая иерархия папок вроде com/somesite/someprogram/… Неужели нельзя скомпилировать java в один файл на ассемблере?

В общем, одни недостатки. А уж если java- или NET программа уйдет в своп, за пару секунлд она оттуда не вылезет.
UFO just landed and posted this here
> Один из них — использование специально подготовленных данных базовой библиотеки, который сокращает время запуска приложения, и потребления памяти при запуске нескольких.

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

> Это разделение на пакеты, модульность. Много файлов можно сохранить в один, архив (с расширением .jar).

Мне попадались программы (Zend Studio), которые устраивают помойку из папок и файлов, прямо таки линукс в миниатюре (bin, doc, share и так далее). Кстати, использование jar ведь замедляет работу программы и увеличивает расход памяти, так rкак 1) нужно вреям на распаковку 2) файл из архива приходится загружать в память, а не просто mmap()'ить.

> А здесь нет разницы, системе все равно, что доставать из свопа, или java, или win32 приложение.

Лукавите. Приложение на c++, хорошо написанное займет намного меньше памяти и быстрее вылезет из свопа. Например программа serviwin от руссиновича для запуска/останова служб запускается быстрее (мгновенно) и работает лучше даже чем встроенная в Windows консоль управления. А если бы она была написана на java, мгновенног озапуска можно было бы не ждать. И то же с многими другими программами.
UFO just landed and posted this here
Ну видимо java-приложения разные бывают, может мне просто кое-как сделанные попадались. Но «быстро грузится» для меня значит — 1-2 секунды на неновом железе и мгновенно на современном, если что. Показывать заставку при запуске и стартовать по полминуты-минуте — это было модно в 95 году, но никак не сегодня.

> для стандартной библиотеки JVM готовит спецфайл для mmap (называется classes.jsa), что бы можно было экономить на времени загрузки и памяти при запуске нескольких приложений

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

> Памяти Serna израсходовало раза в 3 больше.

Видимо, из-за Qt. У меня Qt-приложения вроде Qt designer тоже не очень работают.

И все же мне не нравится современная тенденция делать монстрообразные программы, которые едят 100, 200 и даже больше Мб на не очень сложных задачах (например Amarok, хехе).
UFO just landed and posted this here
> Файлы классов — это лишь промежуточная форма кода/данных перед их адаптации к аппаратной платформе.

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

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

Мне и 95% пользователей это нафиг не нужно. Кстати, код C-программы тоже может располагаться на удалеенном сервере, и даже (если напистаь загрузчик) в БД, но смысла этом я не вижу.
UFO just landed and posted this here
И я все же вам не верю. У меня постоянно запущено минимум 10 программ (лень закрывать а потом запускать) — и если бы это были Java-/или NET-программы, все бы притормаживало. Для java-программы откушать 100-200 Мб-милое дело, да и освобождать она их никогда не спешит, там же система выделения памяти довольно-таки странная, память не освобождается сразу, а с помощью GC — а это занчит подтормаживания в случайный момент времни (Опера тоже кстати похоже этим страдает, хехе). Да и те оптимизации. которые вы упоминаете, вряд ли все разработчики используют.
UFO just landed and posted this here
И вы считаете номальным 17 Мб на helloWorld? Что там такого есть на 17 Мб? 17 миллионов классов что ли? О чем это может говорить, если не о том, что вопросы оптимизации при разработке в расчет просто не принимались? А, подозреваю просто ставились цели как можно быстрее выпустить продукт и захватить рынок.

Я сейчас померял HelloWorld на php под Windows — 6Мб Private Bytes. PHP, у которого более сложное устройство (например там нет строгой типизации, значит он должен быть еще менее эффективен) — и тот ест меньше памяти.

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

А неужели вызов функции free() или аналогичной так уж ресурсоемок? Всего то, переписать пару указателей, пометить память как занятую и поместить в соотв. список.

> GC позволяет эффективнее использовать ресурсы.

Вы уверены? Может он просто позволяет не заморачиваться с ручным освобождением памяти? И я сильно сомневаюсь насчет эффективности. Перебор всех объектов (а их могут быть миллионы, не так ли?) с поиском неиспользуемых никак нельзя назвать эффективным (по моему).

UFO just landed and posted this here
> Я вообще на PHP пишу (там тоже быстро))), а Java/.NET может и приемлемо работают, когда одна программа запущена, но у меня такого не бывает. И еще они заупскаются немгновено (а иногда очень немгновенно) и это неприемлемо.

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

> А у java-программ дурацкая иерархия папок вроде com/somesite/someprogram/…

Модульный подход намного логичнее, чем тонны Сишных/ПХПшных инклюдов.

> Неужели нельзя скомпилировать java в один файл на ассемблере?

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

> Процессор и память стоят дешевле, чем программист.

А студент стоит дешевле нормального разработчика (хотя нормальным такого разработчика-халявщика не назовешь). Наберем студентов?

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

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

А зачем нужны пакеты? А сложности с инклюдами — это недостаток Си, согласен все эти хедеры лишняя муть. Зато у Явы другой недостаток — принцип формирования имени пакета и пути к нему через тонну подпапок жестко прописан. И все эти com.sun.java… — выглядят коряво, лишние сущности.

Вы когда-нибудь писали на C99? Скажу я Вам — это ад! Есть подмножество отличий C от C++, которые просто необходимы для написания понятного maintainable кода. Это, например, namespace, определение переменных в любом месте кода, классы, шаблоны (возможно забыл что-то еще).
> Вы когда-нибудь писали на C99? Скажу я Вам — это ад!

Во избежание геморроя использую GNU89, ибо в GCC C99 реализован не полностью.

> Есть подмножество отличий C от C++, которые просто необходимы для написания понятного maintainable кода. Это, например, namespace, определение переменных в любом месте кода, классы, шаблоны (возможно забыл что-то еще).

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

вещи вроде x = y+z или object->method() на любом языке вынглядят почти одинаково, но на C++ они выполняются куда как быстрее. А вот класс написать  — на C++ руки отвалятся пока хедер напишешь.
Сколько классы пишу, руки еще не отвалились :)
А мне вот было бы лень, после изучения php мне C++ перестал нравиться. Он очень даже «неслдакий» по моему. И писать например заголовок метода дважды (определение, а потом объявление) — на мой взгляд излишний труд, лишние буквы печатать, а это я не люблю))
UFO just landed and posted this here
Вы лукавите. Я имел в виду, то что .h файлы почти всегда можно сгенерировать из .cpp файлов, это может сделать железяка, нет смысла тратить на это время живого человека.
UFO just landed and posted this here
Можно пример непростого случая?
UFO just landed and posted this here
UFO just landed and posted this here
Вы хитрите, вы спрятали куда-то все объявление полей класса! Конечно, чтобы отменить хедеры, часть информации придется перенести в .cpp файл.

Получится примерно так:

class A extends A_parent {
SomeType x;
void show() {
… тут код
}
}

Всяко лучше чем 2 файла писать! меня например раздражает необходимость писать заголовок метода или функции по 2 раза, в объявлении или определении, прямо как будто Страуструп решил поиздеваться над программистом! И еще злит то, что надо функции определять/объявлять до их использования, а ведь удобнее например написать сначала main() а потом те функции которые из нее вызываются — а вот хрен.

p.s. И еще я сейчас подумал — можно было бы убрать инклюды, сделать вместо них файл autoload.conf и подключать нужные файлы компилятором автоматически, при использовании какого-то класса автоматически подключается его определение.

Возможно, раньше это сложно было бы реализовать из-за слабого железа, или (что более вероятно) из-за бездумности создатлей языка, но сейчас давно можно было бы реализовать то, что я написал. Только вот никто этого не сделает, так как Си++ уже забросили. Жаль.
Хорошая идея, я бы только добавил автоматический импорт по определенным правилам, к примеру при использовании какого-то класса — можно было бы сэкономить еще и на написании директив import
UFO just landed and posted this here
Не, это не аргумент. Чтобы быстрее компилировалось, можно использовать различные кеширования. прекомпилированные хедеры, и так далее. Тем более что компьютеры становятся только мощнее.
А вот писать избыточный код — неинтересно. Мне кажется время программиста тут более важный фактор.
> Для PHP это не нужно, т.к. там используется связывание в момент использования [метода]/исполнения

В 99,999% случаев понять, какая функция будет вызвана, можно в момент парсинга программы, так как 1) как правило в приложении нет 2 функций с одинаковым именем и 2) почти все функции вызываются по имени, которое явно проиписано, а не берется из переменной или еще откуда-то. На самом деле, связывать в момент компиляции скрипта было бы грамотнее, так как позднее связывание в основном никому не нужно, а вот производительность подрывает. Глупо.

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

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

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

Я слышал. в современных IDE тянуть скроллбар никуда не надо. Список методов обычно выводится слева от кода.

И еще, я же не предлагал полностью отменить .h файлы, их можно генерировать компилятором из файлов с кодом, если они нужны например для создания SDK.
> «Нельзя, они же будут инлайновыми».

Как показывает Disassembly, тот же компилятор MSVC с настройками по умолчанию (без ключа /Ob1) встраивает не только inline-функции, но и другие, которые сам посчитает нужным встроить. Так что Вы зря переживаете по этому поводу.

> «Я слышал. в современных IDE тянуть скроллбар никуда не надо. Список методов обычно выводится слева от кода».

Хм, странно, у меня в notepad.exe ничего подобного нет…
А notepad можно назвать IDE?)) В любом случае сомнительная возможность пользователям блокнота постмотреть header-файл не стоит лишнего труда. .h файлы должны генерирорваться без участия человека.

Ну и объявлять функции в коде класа глупо  — они будут продублированы в нескольких объектных модулях, я бы не хотел делать такое фуфло, где есть много повторяющегося кода. Одно дело — маленькие функции, другое — полноценнные.

Я более чем уверен, что если в коде функции будет к примеру хоть один цикл, то компилятор не станет делать её подставляемой, так как в этом случае издержки на занесение параметров в стек и вызов функции командой call будут уступать выполнению самой функции (цикл породит гораздо более сложный asm-код). Посему не думаю, что эта функция будет многократно скопирована.
Представим что есть файл class1.cpp с классом Class1 и определенной в нем функцией весом килобайт в 50. этот файл инклудится допустим из 10 других файлов. Вопрос: в какой объектный модуль будет помещен код функции? Я думаю, во все 10 + в class1.o

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

Гм, а вы представляете как присходит компиляция и сборка программы?
Если функция у нас «килобайт в 50», то компилятор явно не станет копировать код в места вызова. А значит по какому-то адресу он поместит её код, а в месте вызова поставит call. Да, код функции будет повторяться во всех объектных файлах, где она юзается, но компоновщик на стадии линковки оставит в бинарнике только одну версию этога метода, о чем упомянуто выше в моей статье.
Проблема в том что не все компиляторы поддержат такой хитрый трюк, например gcc может и не поддержать, следовательно все таки много кода в заголовок класса совать не стоит.
Я писал, что сложность функции — довольно субъективное понятие. И если GCC не может с этим справиться, то наверное это его проблемы… Другие ведь могут!
На чем предлагаете писать desktop-игры к примеру?
UFO just landed and posted this here
Главная причина использования Си++ в геймдеве — большое количество относительно дешевых кодеров и тот факт, что основная «игровая» ОС — Windows вовсю использует этот язык.
Разработчики Unreal, например, присматриваются к Haskell:
www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/sweeny.pdf
UFO just landed and posted this here
Разработчик, знающий/применяющий только один язык — хреновый разработчик. Настоящий профессионал должен знать пару десятков языков различных парадигм и уметь самому создавать языки под задачу, только тогда он может правильно выбрать себе инструмент (предвидя вопрос про себя скажу, что я столько не знаю, но к этому стремлюсь).
Если бы нужна была именно скорость — использовали бы Си, он все равно быстрее.
Ну да, чего уж там? Давайте учить пару сотен языков… Если попытаетесь знать все языки, в совершенстве не будете знать ни один. Есть люди, такие как Вы, которые расширяют свои познания, а есть, которые углубляют. Нужно найти «золотую середину»!

А главное — упор нужно делать не на языки, а на алгоритмы.
> Ну да, чего уж там? Давайте учить пару сотен языков…

Это лишне.

> Если попытаетесь знать все языки, в совершенстве не будете знать ни один.

Есть вариант, что «в совершенстве» (кстати, что под этим понимается?) их знать и не надо будет: то, что долго и неудобно писать на одном языке почти наверняка просто написать на другом. А склеить все можно с помощью трансляторов и FFI.

> А главное — упор нужно делать не на языки, а на алгоритмы.

Алгоритм мало придумать — его надо реализовать. И желательно, чтобы реализация тратила минимум времени и средств. Поэтому — каждой задаче либо классу задач нужно искать свой инструмент, а если понадобиться — написать его самому.
> «…„в совершенстве” (кстати, что под этим понимается?)…»

В том-то и дело, что это понятие крайне субъективно, и у каждого оно своё. Не хватало еще разводить демагогию по этому поводу. И так холиварим давно уже не по теме.

> «Алгоритм мало придумать — его надо реализовать. И желательно, чтобы реализация тратила минимум времени и средств».

Что-то в этом есть, однако без хорошего понимания алгоритмизации никакое знание языков вам не поможет — все программы будут медленными (будь они написаны на С++ или, к примеру, на PHP :)
> Что-то в этом есть, однако без хорошего понимания алгоритмизации никакое знание языков вам не поможет — все программы будут медленными (будь они написаны на С++ или, к примеру, на PHP :)

Я с этим не спорю. Однако знание нескольких языков позволяет максимально полно использовать их сильные стороны, избегая слабых.
Плюс, алгоритмы могут находиться на разных уровнях в контексте решаемой задачи: идеальный вариант — решать прикладную задачу в терминах самой задачи, а Си++ слишком низкоуровневый и слишком ограниченный, чтобы это было возможно на нем.
Вы представляете себе разницу между C и С++? Она не так уж велика. На C++ получается примерно такая же производжительность, если не увлекаться херней вроде STL.

Более того, когда пишешь на C, постоянно создается ощущение что пытаешься с помощью структур и функций имитировать объект… только вот зачем имитировать если можно взять C++ и написать нормальным образом?
UFO just landed and posted this here
А если мы сделаем вектор из объектов?)) Да еще и сложноустроенных? А как он копирует объекты или строки — физическим созданием копии строки в памяти или через подсчет ссылок? По моему так авторы там не заморачивались с оптимизациями.

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

И еще STL не очень то синтаксически сладок, все эти шаблоны, итераторы… с ходу и не разберешься. Хотя конечно может я о нем слишком резко отозвался. Но по моему это тот случай когда в погоне за универсальностью жертвуют всем осталдьным.
UFO just landed and posted this here
UFO just landed and posted this here
Итераторы итераторами, но вообще, если смотреть более широко, STL может вносить заметный оверхед по сравнению с «ручным» кодом. Иначе, например, гейм-девелоперы не изобретали бы ее аналоги для большей производительности.
> Вы представляете себе разницу между C и С++? Она не так уж велика. На C++ получается примерно такая же производжительность, если не увлекаться херней вроде STL.

Разницу очень даже представляю: Си прекрасный «высокоуровневый ассемблер» и идеальный язык для промежуточной трансляции DSL. А Си++ — недоразумение, претендующее на универсальность, но в итоге нормально не делающий ничего. Он слишком ограниченный и низкоуровневый, чтобы использовать его для эффективного решения прикладных задач, и слишком сложный большой, чтобы использовать его для традиционных сишных задач: программирование для микроконтроллеров, написание драйверов и ОС, и другие, требующих прямой работы с железом. Так что разница огромна.

И «херней вроде» не увлекаться не получиться, со временем проект обрастает точно такой же херней, только самописной и намного хуже, чем STL отлаженной.

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

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

Язык должен быть простым и мощным, а Си++ под эти критерии не походит никак.
Слово «гандон», надо полагать, происходит от двух подряд идущих букв грузинского алфавита: соответственно «ган» გ и «дон» დ. Поэтому пишется это слово через «а».

А написал я это к тому, что по-моему пора прекратить холивар.
> А написал я это к тому, что по-моему пора прекратить холивар.

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

Что за бред? А на Си проект превращается в кашу из структур, функций и глобальных переменных. Убиться можно.

А уж если вручную пистаь все malloc()'и — вообще издевательство над человеком, нужно автоматическое управление памятью.
> Что за бред? А на Си проект превращается в кашу из структур, функций и глобальных переменных. Убиться можно.

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

Неправда. Разница есть.
Фундаментально проблемы одни и те же, Си++ лишь повышает «порог нечувствительности» к этим проблемам.
> А Си++ — недоразумение, претендующее на универсальность, но в итоге нормально не делающий ничего.

Повторюсь: сколько я не пытался написать что-то на Си — всегда получались структуры и набор функций для работы с ними — то есть объекты — но с ними удобнее работаьт в Си++. Не понимаю, что удобного в Си. Или вы фанат глобальных переменных и ручной работы с указателями и ручного управления памятью? Хотя в драйверах наверно выгоднее действительно этим заниматься вручную.

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

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

> то есть объекты — но с ними удобнее работаьт в Си++.

По сравнению с CLOS объектная система Си++ ограничена.

> Не понимаю, что удобного в Си.

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

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

Я как раз за то, чтобы не задумываться о таких вещах, а сосредотачиваться на задаче.

> Возможно, Си++ вызывает неприязнь громоздким синтаксисом, он не без недостатков,

Его сложность значительно превышает потенциальную выгоду от его применения.
Формальная грамматика Си++ в книжке Страуструпа занимает около 20 страниц.
У Си++ переусложненная и избыточная система типов, которая — рассадник трудноуловимых ошибок.
Самая главная моя претензия к Си++ — невозможность расширить синтаксис.

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

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

Бррр… писать свой язык под каждую задачу — сомнительное удовольствие.

> Самая главная моя претензия к Си++ — невозможность расширить синтаксис.

Да, это бы хорошо было. Но я против того чтобы это «расширение» делалось в рантайме, ценой потери производительности.
> Бррр… писать свой язык под каждую задачу — сомнительное удовольствие.

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

> Да, это бы хорошо было. Но я против того чтобы это «расширение» делалось в рантайме, ценой потери производительности.

Производительность — не самое главное. Если язык позволяет сэкономить на разработке время и деньги, то будут использовать его. Факт существования тормозной джавы — показателен.
UFO just landed and posted this here
Сам глубоко не разбирался, насколько могу сказать из общения с товарищами, причина — в реализации Java-машины, а точнее — компиляции байт-кода в инструкции конкретного железа.
Разбираться глубже мне только предстоит, пока это на уровне ОБС.
Кстати, почиатл недавно немного про bison  — в принципе создать новый язык (на основе Си) не такая уж и нереальная задача, как кажется
С помощью flex&bison не так уж сложно создать и язык с отличающимся синтаксисом. Вопрос в том, нужно ли это, когда существует много разных легковесных скриптовых языков. Взять хотя бы тот же Lua или Squirrel…
> С помощью flex&bison не так уж сложно создать и язык с отличающимся синтаксисом.

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

> Вопрос в том, нужно ли это, когда существует много разных легковесных скриптовых языков. Взять хотя бы тот же Lua или Squirrel…

Писать языки, отличающиеся только синтаксисом — действительно не нужно. И речь шла не о скриптовых, а о полноценных компилируемых языках, чтобы получать от компилятора либо native, либо сишный код.
Такой подход, называемый Language Oriented Programming, сокращает количество операций, выполняемых вручную, активно используя генераторы кода и метапрограммирование. Пусть это не всегда применимо, но знать разные подходы к разработке должен каждый, кто хочет работать эффективно.
По теме DSL литературы не очень много, недавно нашел описание паттернов LOP:

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

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

> Никто Вас не заставляет пользоваться препроцессором, равно как и самим С++

Дело не в моих предпочтениях, а в объективных недостатках языка. Собственно из-за них его и вытесняют более динамичные C# и Java.
ну, за это конечно спасибо :)
Эта задача — простая, математика там — элементарная. Все упирается исключительно в выбор языка, на котором будет написан компилятор. В качестве таких языков обычно выбирают Lisp, OCaml, Ruby, Forth и вот еще на хабре узнал про IO, который обязательно выучу, как только добью Форт и OCaml.
Неправда, главная причина — производительность, Хаскелл намного меленнее C++. Кстати, чуваки в пдф по ссылке считают нормальным 30 фпс — халявщики, в общем.
> Неправда, главная причина — производительность, Хаскелл намного меленнее C++

А Си — быстрее Си++. Почему не пишут на нем?

> Кстати, чуваки в пдф по ссылке считают нормальным 30 фпс — халявщики, в общем.

Эти чуваки — разработчики одного из самых технологически совершенных движков. И 30 ФПС — вполне достаточно, при условии, что они выдаются стабильно.
> «А Си — быстрее Си++. Почему не пишут на нем?»

Потому что, опять же, С++ — это золотая середина. С одной стороны он позволяет добиться высокой скорости работы программ, а с другой — предоставляет возможность эффективно работать с большими проектами.
UFO just landed and posted this here
В том примекре предельно упрощенный код, на котором действительно есть какая-то оптимизация. Но в реальности, на много раз унаследованных классах со сложной структурой, затраты на компиляцию могут и не окупиться. В реальности мы имеем ява-машину, которая на HelloWolrd программе ест то ли 10 то ли 19 Мб памяти (точно не помню, давно это было).
UFO just landed and posted this here
Меня не устраивает «достоточно», я хочу скорость «как у C-программ»
UFO just landed and posted this here
Ну вот и давайте, Вы напишете небольшую трехмерную desktop-игру на Java+C, а я потестирую её на производительность на своем AMD Duron 1200…
Да, кстати обе вышли когда ваш Duron был только в проекте.
Так вот, я должен заметить, Vampire: The Masquerade шел на моем Duron не лучшим образом, в то время как игры с графикой явно не хуже (от той же EA) у меня отлично работали.

Не думаю, что EA пишет игры на Java (с учетом того, что они даже придумали свою версию STL)
А вообще, это глупый, холиварный спор.
UFO just landed and posted this here
В случае с многоядерным процессором ни С/С++, ни Java не сравнятся с Haskell по удобству распараллеливания.
Подозреваю, C на 1 ядре будет быстрее хаскелла на 8.
Пока что, наверное, да…
Не так всё плохо, зависит от задач. К примеру, парсер Си на Haskell уступил gcc всего в три раза при разборе исходников ядра Linux (пруфлинк) при несравнимом удобстве разработки и дальнейшего использования.
В три раза — это не так уж и хорошо :(
Это частная задача, в общем случае думаю результат будет хуже.
Блин. Не сочтите за наезд, но даже ссылка на всеми пинаемый shootout выглядит убедительнее, чем размышления о некоем сферическом общем случае в вакууме. Для уровня абстракции Hakell отставание от Си в 2–5 раз — это огромное достижение авторов компилятора и рантайма, а резервы оптимизации далеко не исчерпаны.
Вот! Пусть не игры, но все современные приложения, ОС и прочий софт должны тестироваться и разрабатываться на таком железе, а не на 8-ядерных компьютерах. Чтобы у разработчиков не возникало нездорового соблазна использовать тормозные технологии.
Поддержка любого большого проекта на Си++ со временем превращается в ад. В принципе, это качается всех императивных языков. Я уже не говорю невероятно сложной отладке.
> И 30 ФПС — вполне достаточно, при условии, что они выдаются стабильно.

Нифига. лучше столько же фпс, сколько кадров в секунду отображает монитор. Эот разработчики придумывают себе оправдания, чтобы писать быстро а не качественно.
Сложность поддержки и отладки Си++ кода растет экспоненциально. Когда у тебя тысячи объектов, каждый из которых изменяет состояние любых сосредоточиться на оптимизации кода невозможно — нужно хотя бы самые заметные ошибки зализать.
Насчет Haskell я спорить не буду — скорее всего за функциональным программированием будущее (хотя я, к сожалению, с ним пока что практически не знаком). Но, если я не ошибаюсь, программы на Haskell все еще уступают по скорости аналогичным программам на C++… Я не буду отстаивать эту точку зрения, потому как просто где-то слышал такое, и буду рад, если сейчас это уже не так.
UFO just landed and posted this here
UFO just landed and posted this here
Я согласен. Но я такой же начинающий, как и те, для кого я пишу. И я сам по ходу разбираюсь понемногу.

А вот Вы, как и многие другие, заигнорили мой совет: «Профессионалам … можно дальше не читать» :) Не в обиду.
Я не понял, где тут, собственно, миф то?
«Миф» выделен полужирным в тексте и состоит в том, что все-таки не всегда удается «безболезненно» определить inline-метод вне объявления класса. Эккель, к примеру, пишет, что в отличие от обычных методов, inline-методы нужно определять прямо в хедере, но мой мозг благополучно «пропустил» этот факт и вспомнил о нем лишь тогда, когда столкнулся на практике.
Для Вас, видимо, это не миф, а многие до сих пор об этом не подозревают.

А вообще не нужно относиться к слову «миф», как к ключевому. Я об этом писал: «Оно [название], естественно, появилось не случайно, однако и не совсем соответствует сути».
UFO just landed and posted this here
Да, Вы правы. Но суть в том, что многие не задумываются об этом до тех пор, пока не возникнут проблемы. У довольно большого числа людей психология в духе «зачем мне знать, как это устроено, если все и так работает?».
Если на то пошло, то некоторые авторитетные авторы пишут, что слово inline вообще нигде и никогда использовать не нужно.
Смысл в том, что любой разумный оптимизатор и сам сделает подстановку тела, если в этом есть смысл.

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

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

Предположим, есть хэдер H. Есть также модули A и В, которые включают в себя этот хэдер. Теперь в силу каких-то причин нам нужно добавить в хэдер какую-нибудь короткую функцию, при этом создавать соответствующий CPP-модуль не хочется (лень :) ).

Если просто добавить фукнцию, будет ошибка линковки: duplicate definition in A, B.
Если же фукнцию объявить как инлайновую, то всё отлично скушается.

Впрочем, можно просто создать безымянное пространство имён. По идее, это должно сработать (но в хэдерах не пробовал).
А вот мы не так давно на каких-то вычислениях (компилятор — gcc) обнаруживали, что волшебное слово register таки повышает скорость вычислений раза в 4. Так что иногда их вставлять всё-таки нужно, чтобы переубедить неразумный компилятор :)
UFO just landed and posted this here
Нет, на абсолютно реальных.
Статья хорошая, но подобная тема хорошо изжевана у Герба Саттера в «Новых сложных задачах по С++». Неплохо было бы упоминуть об этом.
У вас же в основном тема полностью не раскрыта. Также не сказано о различных типах инлайна (даже во время компиляции). А просто указана типичная ошибка программирования, когда вы инлайните не метод, а его объявления. Конечно, если вызовы будут в разных единицах трансляции — это приведет в большим проблемам :)
Когда прочитаю книгу Герба Саттера, упомяну ;)
«Почему?! Всё достаточно просто: определение подставляемого метода и её вызов находятся в разных единицах трансляции!»

В корне не верно, единица трансляции — это cpp файл, который был обработан препроцессором и будет передан линковщику. Если вы инклудите A.h в A.cpp, то A.h и A.cpp образуют одну единицу трансляции. Если вы инклудите A.h в B.cpp, то они образуют вторую, отличную от первой единицу трансляции.
Впрочем ниже, вы все правильно расписали. Поспешил :)
А это ещё что за коды??? Хабр испортился или статья?
Подставляемая функция объявляется достаточно просто:

<font size="2" face="Courier New" color="black"><font color="#0000ff">inline</font> <font color="#0000ff">void</font> foo<font style="color: #008000;">(</font><font color="#0000ff">int</font> <font color="#000040">&</font> _i<font style="color: #008000;">)</font><br /><font style="color: #008000;">{</font><br />  _i<font color="#000040">++</font><font color="#008080">;</font><br /><font style="color: #008000;">}</font></font>

Но речь сейчас не об этом. Мы рассмотрим использование подставляемых методов 
Раньше на хабре не было подсветки синтаксиса для кода. Выживали, как могли. Сейчас уже западло менять, учитывая, что ломал не я :)
Хреново. Поменять то не так уж трудно на самом деле, делов-то, пара минут. А кому-то пригодится :)
Вот даже тут можно исправить, просто вставляя эти коды в поле ввода комментария без обрамляющих тэгов, и на выходе получается красиво:
Заголовок спойлера
1.
inline void foo(int & _i)
{
_i++;
}


2.
// InlineTest.cpp

#include
#include

struct A
{
inline void foo() { std::cout << «A::foo()» << std::endl; }
};

struct B
{
inline void foo();
};

void B::foo()
{
std::cout << «B::foo()» << std::endl;
}

int main()
{
A a; B b;
a.foo();
b.foo();
return EXIT_SUCCESS;
}


3.
// A.h

#ifndef _A_H_
#define _A_H_

class A
{
public:
inline void foo();
};

#endif // _A_H_


// A.cpp

#include «A.h»

#include

void A::foo()
{
std::cout << «A::foo()» << std::endl;
}


// main.cpp

#include
#include
#include «A.h»

int main()
{
A a;
a.foo();

return EXIT_SUCCESS;
}


4.
// A.h

#ifndef _A_H_
#define _A_H_

class A
{
public:
inline void foo();
void bar();
};

#endif // _A_H_


// A.cpp

#include «A.h»

#include

void A::foo()
{
std::cout << «A::foo()» << std::endl;
}

void A::bar()
{
std::cout << «A::bar()» << std::endl;
foo();
}


// main.cpp

#include
#include
#include «A.h»

int main()
{
A a;
a.foo();

return EXIT_SUCCESS;
}



Где администраторы хабра? Они что, слепые?

Sign up to leave a comment.

Articles