LINUX.ORG.RU

Может кто нибудь показать красоту с++?

 ,


7

9

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

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

Но и в си я могу сделать то же самое, объявив их static.

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

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

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

Это понятно, что непереносимые фичи, типа рекурсии в inline, в кросс-платформенном коде лучше не использовать. Тем более, что практической пользы от inline нет: компилятор всё равно делает, что хочет, не обращая внимания на эту инструкцию. Так, gcc без оптимизации не инлайнит inline, а с оптимизацией — инлайнит не inline и в си, и в си++.

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

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

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

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

Это понятно, что непереносимые фичи, типа рекурсии в inline, в кросс-платформенном коде лучше не использовать.

Речь даже не об этом. До недавнего VC++ мог прожевать даже код шаблонов с синтаксическими ошибками внутри. Потому, что раньше там была двойная схема обработки шаблонов. Сперва тело шаблона просто запоминалось, а парсилось и обрабатывалось только при инстанциировании. Поэтому если шаблон в конкретной единице трансляции не инстанциировался, то его код мог пройти «компиляцию» даже с ошибками внутри.

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

код мог пройти «компиляцию» даже с ошибками внутри.

Это весело. Типа макросов.

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

Но ведь у gcc есть always_inline

А еще уверен на 100% аналог есть в MSVC, а еще есть ифдефы=)) Но этто если очень хочется зайнлайнить.

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

Но ведь у gcc есть always_inline

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

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

С точки зрения контроля ресурсов - да, ибо см. коммент выше. С точки зрения собственно реализации алгоритмов же, unique_ptr вполне можно использовать в сколь угодно сложных динамически создаваемых графах. Хоть сразу в boost::graph<> клади.

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

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

Остаётся вопрос производительности. Тут всё зависит от ситуции. Но тут мы вспоминаем, что shared_ptr сам по себе относительно медлительная штука...

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

Мне, конечно, далеко до ваших высот абстрактного мышления и размышлений о полноте по Тьюрингу. Поэтому может вы объясните как, например, обходится без аналогов shared_ptr, скажем, при реализации publish-subsciber. Когда publish-ер один, а subscriber-ов много и каждый subscriber может ре-publish-ить полученное им сообщение в другие топики?

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

см. коммент выше.

Этот?

next_time> граф владения, согласно RAII, равен графу порождения. В этом весь смысл этого паттерна.

Я правильно понимаю, что весь смысл RAII - делать граф владения равным графу порождения?

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

До недавнего VC++ мог прожевать даже код шаблонов с синтаксическими ошибками

Зато сейчас он даже вот этого прожевать не может:

#include <cstdio>
#include <utility>

template<typename ... Args>
inline void oops(Args&& ... args)
{
  if constexpr (true) std::fprintf(stderr, ":-)", std::forward<Args>(args)...);
}

int main()
{
  oops(":-)");
}

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

Говорит, мол

fatal error C1001: An internal error has occurred in the compiler. (compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\c\parsetree.cpp', line 1691) To work around this problem, try simplifying or changing the program near the locations listed above.

Лол :-)

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

Какая версия компилятора? У меня работает .

В стандарте поставил: «Последний черновик стандарта ISO C++»

1>------ Перестроение всех файлов начато: проект: Project134, Конфигурация: Release Win32 ------
1>main.cpp
1>c:\users\fsb4000\documents\visual studio 2017\projects\project134\project134\main.cpp(7): warning C4474: "fprintf" : для строки формата передано слишком много аргументов
1>c:\users\fsb4000\documents\visual studio 2017\projects\project134\project134\main.cpp(7): note: заполнители и их параметры ожидают переменное число аргументов в количестве 0, но было предоставлено 1
1>c:\users\fsb4000\documents\visual studio 2017\projects\project134\project134\main.cpp(12): note: выполняется компиляция ссылки на экземпляр шаблон функции "void oops<const char(&)[4]>(const char (&)[4])"
1>Создание кода
1>All 7 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
1>Создание кода завершено
1>Project134.vcxproj -> C:\Users\fsb4000\Documents\Visual Studio 2017\Projects\Project134\Release\Project134.exe
1>Сборка проекта "Project134.vcxproj" завершена.
========== Перестроение всех проектов: успешно: 1, с ошибками: 0, пропущено: 0 ==========
>cl
Оптимизирующий компилятор Microsoft (R) C/C++ версии 19.13.26128 для x86
(C) Корпорация Майкрософт (Microsoft Corporation).  Все права защищены.
>Project134.exe
:-)
fsb4000 ★★★★★
()
Ответ на: комментарий от eao197

Если я вас правильно понял, то это практически описание сигнально-слотового механизма Qt. Работающего безо всяких shared_ptr.

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

Я правильно понимаю, что весь смысл RAII - делать граф владения равным графу порождения?

Это его альтернативное определение, если угодно.

Альтернативное. Понятно. Ты подтвердил комментарий к своему нику.

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

Сигналы-слоты умеют и в асинхронность и в то, что вы выше описали. Но на случай, если вы случайно забыли про фильтрацию (и т.о. хотели уже скорее паттерн «публицист-подписчик»), то тогда в Qt есть система событий (см. класс QEvent), которая и это реализует. Разумеется, безо всяких shared_ptr.

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

Но на случай, если вы случайно забыли про фильтрацию (и т.о. хотели уже скорее паттерн «публицист-подписчик»)

Молодой человек, я не «хотел уже скорее», я именно паттерн publish-subscribe и просил:

Поэтому может вы объясните как, например, обходится без аналогов shared_ptr, скажем, при реализации publish-subscriber. Когда publish-ер один, а subscriber-ов много и каждый subscriber может ре-publish-ить полученное им сообщение в другие топики?

А потом просил еще раз внимательно перечитать. Но вы не осилили написанное.

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

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

Какая версия компилятора?

Microsoft (R) C/C++ Optimizing Compiler Version 19.12.25835 for x86

У меня работает .

О, сейчас обновлюсь, спасибо! :-)

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

рекурсивный inline.

Это как? tail-call optimization что ли?

Ну, мой пример был совсем банальный:

int f(int n)
{
  return n!=1?f(n-1):1;
}

int main()
{
  return f(3);
}

оптимизатор просто превратил в

int main()
{
  return 1;
}
aureliano15 ★★
()
Последнее исправление: aureliano15 (всего исправлений: 1)
Ответ на: комментарий от next_time

Ок, «публицист-подписчик» в Qt - система эвентов.

Отлично, теперь расскажите, как систем эвентов Qt справляется с асинхронной доставкой эвентов по схеме 1:N (т.е. один отправитель - много получателей). И, как обязательное условие, как справляется с ситуацией, в которой каждый получатель вновь отсылает этот же эвент по схеме 1:N другой группе получателей.

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

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

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

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

чем же вас так оскорбило

Тебе показалось.

новое для вас?

В твоих утверждениях не оказалось ничего для меня нового.

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

Ну это не столько inline сколько constexpr.

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

#include <stdlib.h>

int f(int n)
{
  return n>1?f(n-1):n;
}

int main(int argc, char** argv)
{
  int n = 0;
  if(argc>1)
    n = atoi(argv[1]);
  return f(n);
}

И при опциях компиляции -O3 и -S получил такой выхлоп:

	.file	"test_recurs.c"
	.text
	.p2align 4,,15
	.globl	f
	.type	f, @function
f:
.LFB13:
	.cfi_startproc
	testl	%edi, %edi
	movl	$1, %eax
	cmovle	%edi, %eax
	ret
	.cfi_endproc
.LFE13:
	.size	f, .-f
	.section	.text.startup,"ax",@progbits
	.p2align 4,,15
	.globl	main
	.type	main, @function
main:
.LFB14:
	.cfi_startproc
	cmpl	$1, %edi
	jg	.L11
	xorl	%eax, %eax
	ret
.L11:
	subq	$8, %rsp
	.cfi_def_cfa_offset 16
	movq	8(%rsi), %rdi
	movl	$10, %edx
	xorl	%esi, %esi
	call	strtol@PLT
	movl	$1, %edx
	testl	%eax, %eax
	cmovg	%edx, %eax
	popq	%rdx
	.cfi_def_cfa_offset 8
	ret
	.cfi_endproc
.LFE14:
	.size	main, .-main
	.ident	"GCC: (GNU) 7.3.0"
	.section	.note.GNU-stack,"",@progbits

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

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

В код не вчитывался но если правильно понял, это и есть tail call optimization. Кстати компилятор обязан оставлять заинлайненную функцию как отдельный символ на случай, если кому-то другому вдруг взбредет в голову сделать ей &f.

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

это и есть tail call optimization

Ну да.

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

Да, всё правильно. Чуток ошибся. Вставил в си-код static перед f, и она исчезла из asm'а.

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

«как систем эвентов Qt справляется с асинхронной доставкой эвентов по схеме 1:N (т.е. один отправитель - много получателей). И, как обязательное условие, как справляется с ситуацией, в которой каждый получатель вновь отсылает этот же эвент по схеме 1:N другой группе получателей.»

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

«обходится без аналогов shared_ptr.»

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

«с асинхронной доставкой эвентов»

умеет, насколько полноценно сами решайте:

https://wiki.qt.io/Threads_Events_QObjects

«So far we've always talked about „the event loop“, taking somehow per granted that there's only one event loop in a Qt application. This is not the case: QThread objects can start thread-local event loops running in the threads they represent. Therefore, we say that the main event loop is the one created by the thread which invoked main(), and started with QCoreApplication::exec() (which must be called from that thread). This is also called the GUI thread, because it's the only thread in which GUI-related operations are allowed. A QThread local event loop can be started instead by calling QThread::exec() (inside its run() method): »

Ну и

bool QCoreApplication::notify(QObject * receiver, QEvent * event)

Sends event to receiver: receiver->event(event). Returns the value that is returned from the receiver's event handler. Note that this function is called for all events sent to any object in any thread.

For certain types of events (e.g. mouse and key events), the event will be propagated to the receiver's parent and so on up to the top-level object if the receiver is not interested in the event (i.e., it returns false).

There are five different ways that events can be processed; reimplementing this virtual function is just one of them. All five approaches are listed below:

Reimplementing paintEvent(), mousePressEvent() and so on. This is the commonest, easiest and least powerful way. Reimplementing this function. This is very powerful, providing complete control; but only one subclass can be active at a time. Installing an event filter on QCoreApplication::instance(). Such an event filter is able to process all events for all widgets, so it's just as powerful as reimplementing notify(); furthermore, it's possible to have more than one application-global event filter. Global event filters even see mouse events for disabled widgets. Note that application event filters are only called for objects that live in the main thread. Reimplementing QObject::event() (as QWidget does). If you do this you get Tab key presses, and you get to see the events before any widget-specific event filters. Installing an event filter on the object. Such an event filter gets all the events, including Tab and Shift+Tab key press events, as long as they do not change the focus widget.

See also QObject::event() and installEventFilter().

void QCoreApplication::postEvent(QObject * receiver, QEvent * event)

Adds the event event, with the object receiver as the receiver of the event, to an event queue and returns immediately.

The event must be allocated on the heap since the post event queue will take ownership of the event and delete it once it has been posted.

When control returns to the main event loop, all events that are stored in the queue will be sent using the notify() function.

Events are processed in the order posted. For more control over the processing order, use the postEvent() overload below, which takes a priority argument. This function posts all event with a Qt::NormalEventPriority.

Note: This function is thread-safe.

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

Доброго времени суток.

Нормально все удается. Как? Надо смотреть сорцы. Но проблем нет. И там свой шаред птр есть.

deep-purple ★★★★★
()
Ответ на: комментарий от next_time

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

PS. И речь вообще шла не о Qt. Если вы уж приводите «доказательство» на примере Qt, то будьте добры объяснить, как там это работает. Если работает, конечно.

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

как там это работает

что именно? удаление неиспользованных ресурсов? элементарно - как только экземпляр QEvent будет обработан всеми зарегистрированными на него обработчиками, он будет удалён. За управление QEvent-ами отвечает QApplication

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

Или вы хотите, чтобы я вам в 2-х словах объяснил все принципы работы системы эвентов в Qt? Так не получится, она не настолько проста.

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

Или вы хотите, чтобы я вам в 2-х словах объяснил все принципы работы системы эвентов в Qt?

Я хочу, чтобы вы своими словами объяснили, как вы сможете реализовать вот такое без использования сборщика мусора или подсчета ссылок (подсчет ссылок — это и есть shared_ptr).

Ссылки на Qt не прокатывают, ибо:

1. Вы не показали, что там в принципе такое возможно. Например, из процитированной вами документации не ясно, можно ли вызвать несколько QCoreApplication::notify с одним и тем же QEvent-ом, но разными receiver-ами.

2. Даже если такой вызов разрешен, то вы не объяснили, за счет чего это достигается. Вполне возможно, что там внутри shared_ptr или какая-то его форма. Но вы об этом просто не знаете.

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

Например, из процитированной вами документации не ясно, можно ли вызвать несколько QCoreApplication::notify с одним и тем же QEvent-ом, но разными receiver-ами.

Вы вообще читали что там написано? Вам не notify нужен, а postEvent В любом случае, receiver на самом деле не один, а содержит в себе ссылки на остальных получателей событий, которые могут в результате QEvent получить, а могут не получить, т.к. там сложная система фильтрация (способов фильтрации аж 5 штук)

Даже если такой вызов разрешен, то вы не объяснили, за счет чего это достигается.

я вам это уже разжёвывал: «элементарно - как только экземпляр QEvent будет обработан всеми зарегистрированными на него обработчиками, он будет удалён»

что может быть проще удаления объекта из очереди?

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

Молодой человек, если вам не понятны какие-то слова из тех, что я вам говорю, например «своими словами», «асинхронно», «модель publish-subscribe», то не стесняйтесь, переспросите. Я обязательно объясню, что они означают.

я вам это уже разжёвывал: «элементарно - как только экземпляр QEvent будет обработан всеми зарегистрированными на него обработчиками, он будет удалён»

Как раз в виду асинхронности самый интересный вопрос — это определение момента времени когда «QEvent будет обработан всеми зарегистрированными на него обработчиками». Вот и объясните, как это делается без подсчета ссылок или сборщика мусора.

Если не можете своими словами, то показывайте ссылки на соответствующие места в коде Qt.

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