LINUX.ORG.RU

hpp vs cpp

 ,


0

2

Привет! Вот вброс. какие есть за и против писать все в заголовочных файлах, с шаблонами и без, и писать, использую *.cpp файлы. (ну я хочу поднять эту тему у себя, вот готовлюсь.)

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

hpp подход

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

cpp подход

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

Отдельно предлагаю ответить на вопрос каким образом расширения файла ускоряет или замедляет компиляцию в первую очередь самому себе ;)

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

не понял тебя..

//file foo.hpp
#pragma once

struct foo {
  void do_something() const {}
};

// file bar.hpp
#pragma once

struct bar {
  void do_something() const {}
};

// file main.cpp
#include "foo.hpp"
#include "bar.hpp"

int main(int,char**) {
  foo f;
  bar b;
  f.do_something();
  return 1;
}

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

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

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

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

//file foo.hpp
#pragma once

struct foo {
  void do_something() const;
};

// file foo.cpp
#include "foo.hpp"

void foo::do_something() const {}

// file bar.hpp
#pragma once

struct bar {
  void do_something() const ;
};

// file bar.cpp
#include "bar.hpp"

void bar::do_something() const {}

// file main.cpp
#include "foo.hpp"
#include "bar.hpp"

int main(int,char**) {
  foo f;
  bar b;
  f.do_something();
  return 1;
}
zerhud
() автор топика
Ответ на: комментарий от ox55ff

не )) ну cmake тоже писать надо. так просто пишешь g++ main.cpp и все (ну понятно что могут быть внешние либы например, инклюды, которые так просто не найти, так что можно просто скриптик).

zerhud
() автор топика

hpp подход

не нужна система сборки

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

ускоряет компиляцию

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

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

А в случае кода в cpp не дает?

cpp подход

замедляет сборку

Каким образом?

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

См. выше. Для чего-то сложнее hello world она все равно нужна.

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

Каким образом? При компиляции cpp будут выставленные предупреждения и оптимизации.

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

Ну и как ты думешь, на чем проще будет написать проект с использованием 4-8 thirdparty библиотек, на твоем скриптике или на cmake? А если ты отложишь проект и вернешься через год, вот удовольствие будет разбираться со скриптом…

rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 1)
Ответ на: комментарий от rumgot

Только елси ты все будешь фигачить сам.

тут суть в том, что компилируется одной командой, её просто пишешь один раз в скрипте (тут я подразумеваю что есть тесты и основной executable, поэтому по кол-ву тестов кол-во вызовов компилятора).

А с чего бы?

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

А в случае кода в cpp не дает?

да

Каким образом?

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

zerhud
() автор топика

Все вообще не так.

hpp (отказ от раздельной компиляции) замедляет компиляцию, остальное вообще мимо кассы.

Насчёт системы сборки - если речь о том что один cpp и остальное хидеры, то она ненужна… если в опциях не запутаетесь.

AntonI ★★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от AntonI

замедляет компиляцию

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

если в опциях не запутаетесь

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

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

опа, решение :)

есть еще ipp для тех, кто не определился :)

я просто пытаюсь собрать какие-то плюсы и минусы подходов.

кстати, народ, понятно вообще о каких подходах я говорю? )) а то может мы тут о разном говорим ))

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

остальное вообще мимо кассы

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

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

тут суть в том, что компилируется одной командой, её просто пишешь один раз в скрипте (тут я подразумеваю что есть тесты и основной executable, поэтому по кол-ву тестов кол-во вызовов компилятора).

Как собираешься подключать в свой проект thirdparty библиотеки? Тоже все руками будешь прописывать пути до файлов библиотек и параметры компиляции/линковки?

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

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

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

А в случае кода в cpp не дает? да

Что да? Обоснуй. Код в cpp не проверяется компилятором во время его компиляции? Что ты несешь?

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

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

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

так что можно просто скриптик

Который сразу привяжет проект к ОС, прощай, кроссплатформенность.

В целом, аргумент о ненужности системы сборки работает, если твой проект использует исключительно header-only библиотеки, если нет — вся стройность разваливается.

Плюс замедление сборки.

hobbit ★★★★★
()
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от zerhud

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

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

С другой стороны ты что предлагаешь при использовании твоим проектов 5-10 других либ, тоже их каждый раз собирать? Давай какую нибудь Qt так хранить в виде хедеров. Вот забава будет это пересобирать каждый раз.

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

исключительно header-only библиотеки

ну я об этом. хотя можно написать -lmy_cool_library и это будет работать в большинстве случаев (то есть это уже трабла окружения, но и с системой сборки все точно также).

привяжет проект к ОС

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

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

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

но в целом: доступность всего ast дает больше информации. ты можешь ее использовать явно (requries, static_assert, if constexpr, consteval и прочее) и компилятор тоже может ее использовать.

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

Каким образом?

Инстансы шаблонов не шарятся при линковке насколько я помню, а посему ты будешь перекомпилировать свой std::vector каждый раз в каждом своем .cpp заново

Каким образом? При компиляции cpp будут выставленные предупреждения и оптимизации.

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

sarevok
()
Ответ на: комментарий от hobbit

так что можно просто скриптик

Который сразу привяжет проект к ОС, прощай, кроссплатформенность.

Не, не привяжет :) просто добавится шагов «реквайрементс». Более того, модные потуги убить цмейк тащат с собой разнообразный бред типа пыхтона (потому что пионеры из фаангуглов поголовно страдают NIH синдромом и шатания от gyp к scons ехал градле через градле и далее в meson для сборки плюсовых программ и так добавляют разнообразного ненужно).

А башик уже можно везде запустить.

slackwarrior ★★★★★
()
Последнее исправление: slackwarrior (всего исправлений: 1)
Ответ на: комментарий от faq2

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

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

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

но в целом: доступность всего ast дает больше информации. ты можешь ее использовать явно (requries, static_assert, if constexpr, consteval и прочее) и компилятор тоже может ее использовать.

Это все теория. Может не может… Ты можешь привести реальный пример?

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

Какая тебе разница что тянуть? :) цмейк же тоже не часть винды. А так хоть скрипты писать единообразно без заклинаний на раковине повара и страданий в недошелле cmd.

slackwarrior ★★★★★
()
Последнее исправление: slackwarrior (всего исправлений: 1)
Ответ на: комментарий от KivApple

да :) думаю это причина, по которой и появились cpp файлы вообще.

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

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

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

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

Но это только один прмимер. Так же сть ЦА и нефункциональные требования всех видов и мастей.

faq2
()
Ответ на: комментарий от sarevok

Инстансы шаблонов не шарятся при линковке насколько я помню, а посему ты будешь перекомпилировать свой std::vector каждый раз в каждом своем .cpp заново

Ну я там выше вообще то упомянул про шаблоны.

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

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

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

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

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

Не для всего он подходит (или у тебя шелл на цмейке где-то есть? :)), а если в проекте и так что-то собирается не цмейком (какой-нибудь v8 или буст) — баш это вообще не проблема, одной зависимостью больше, одной меньше. И цмейк никто не мешает пользоваться. Тут нет противоречия.

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

С какого фига ты думаешь, что ты лучше автора решишь, какие ее функции делать встроенными, а какие нет?

Не я, а компилятор

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

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

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

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

линковка это прошлое - оно не совместимо с мощными выразительными языками.

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

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

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

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

sarevok
()
Ответ на: комментарий от rumgot

Можно. А иногда его собрать надо. Весь. И там цмейка нет :) а есть их бустовый велосипед . Который можно дергать из цмейка... а можно их обоих из баша, который просто клей в пайплайне. Вообще нет «одного правильного способа», есть 15 конкурирующих и большинство чем дальше, тем больше про дрочь вприсядку, когда для простого примера вдруг надо поднимать целый сервер или «сендбокс»... хотя рядом есть старый способ, который никуда не делся, просто работает и не делает мозги, в отличие от одептов любого «самого правильного» способа.

slackwarrior ★★★★★
()
Последнее исправление: slackwarrior (всего исправлений: 2)