Pull to refresh

Comments 31

И снова в статье вроде как про C++ представлен на самом деле «C с классами»… Или точнее в данном случае «C с перегрузкой операторов». )
Угу а если будем писать на С++ с шаблонами получим С с шаблонами да? Так можно что угодно вывернуть. Где же тогда тот самый С++ по вашему мнению, который не С с чем-то?
Для начала, если мы пытаемся писать на C++, а не на C, то для строк у нас будет wstring, а не wchar_t *, для массивов vector или array (по ситуации), а не var* и для подсчёта ссылок (кстати про это ещё ниже) shared_ptr. Собственно ручная работа с памятью в прикладном коде на C++ — это практически нонсенс, если не считать пары особых случаев, но тут явно не он. Это вот самые базовые вещи, но даже они тут нарушены. Я уже не говорю о более тонких вещах, типа конструкторов перемещения и т.п.

Ну и наконец насчёт архитектуры в принципе. Вообще идея демонстрировать что-то на C++ реализуя копию поведения JS крайне сомнительна, т.к. тут наблюдается полное противоречие базовых принципов. Медленные ссылочные типы (да ещё и на базе подсчёта ссылок!) очень плохо ложатся на современный C++, базирующийся на семантике перемещения. А вот в том же C это вполне используемый инструмент (за отсутствием других автоматических средств).

Но даже если уж так увлечься идеей копирования JS, то даже это в статье полноценно не реализовано. Потому как в JS в данный тип входят так же ещё и функции. Причём не простые, а реализующие замыкание. Почему то этот вопрос полностью обойдён стороной, хотя на C++ (опять же в отличие от C) он реализуется тривиально.

В общем автор очевидно пишет не на C++, а на «C с перегрузкой операторов». А т.к. данный «диалект» вряд ли полезен хоть где-то, то аналогичное можно сказать и про всю эту статью.
То есть для вас признаком С++ является использование стандартной библиотеки? А если её не используют то это просто С с классами, ссылками, перегрузкой операторов и RAII так (в контексте данной статьи)? Мне почему то кажется, что это не совсем верный подход.
Использовать свои велосипеды вместо стандартной библиотеки языка вполне приемлемо для тех случаев, когда стандартная библиотека почему-то плохо подходит. Однако здесь явно не этот случай.

Кстати, в данном коде велосипеды ещё и кривые. Например подсчёт ссылок реализован не потокобезопасно (а ну да, atomic — это же тоже C++...) и т.д. и т.п.

Ну и надо определиться. Мы тут делаем точную копию JS или не обязательно? В первом случае где тогда замыкания? А во втором случае почему используются не типы значения (и тогда соответственно RAII и семантика перемещения), традиционные для современного C++, а неудобные ссылочные типы (да ещё и с такой реализацией)?

Причём здесь велосипеды? Я ни в коем случае не защищаю код автора статьи (имхо он посредственен, и да я тоже думаю, что нужно использовать средства библиотеки вместо велосипедов) но тем не менее это С++, а не С с классами или операторами. Просто каждый раз как кто-то такое пишет, так и хочется узнать что же тогда для человека С++, сколько фичь нужно использовать из набора С++, чтобы из С с чем то, код превратился в С++. В вашем случае как я понимаю нужно всего лишь использовать STL.
В моём понимание, писать на C++ — это означает использовать все возможности языка, для создания максимально безопасного и быстрого кода. В данном же коде используется только маленькая часть этих возможностей, как раз где-то на уровне возможностей C.
А если используется реализация, которая делает код очень быстрым, но не очень безопасным, и без использования всех возможностей языка это С++? Вы просто уводите куда-то в идеальные случаи, которые практически никогда не встретишь в жизни, и что от этого С++ перестаёт быть С++? К тому же ваше утверждение так и наводит на мысль об оверинжиниринге.
Вообще то как раз более безопасный код чаще всего и самый быстрый. Просто потому, что чем больше ограничений в коде и вычислений на этапе компиляции, тем проще оптимизатору с этим работать.
По-моему оверинжиниринг — это скорее как раз вот это:
#define constructor
…
constructor var () {
Я понимаю, что этим даже «отцы-основатели» баловались (хорошо известный пример из оригинального Bourne Shell'а):
#define BEGIN	{
#define END	}
#define SWITCH	switch(
#define IN	){
#define ENDSW	}
#define FOR	for(
#define WHILE	while(
#define DO	){
#define OD	;}
#define REP	do{
#define PER	}while(
#define DONE	);
#define LOOP	for(;;){
#define POOL	}
Но в современном мире такие вещи вытворять не принято. И уж тем более не принято разносить реализацию одного класса по разным файлам с помощью #include (иногда приходится, да, но обычно всё-так стараются так не делать). Да и вообще количество WTFов при чтении этого творения зашкаливает.

Ошибки, выдающиеся в strdout — это ведь такой идеоматичный C++, правда?

P.S. Вообще же, что бы распарсить JSON на C++ ничего этого не нужно. Возьмите json_parser и не мучайтесь.
Признаком C++ является вменяемый код, в первую очередь. Где для UTF-16 используется не четырёхбайтный wchar_t, а двухбайтовый char16_t, где не изобретается без нужды велосипед (что заодно позволяет не порождать ошибок вида if (size < size) return -1; ), где грамотно используется const и права доступа (что будет со всеми вашими построениями, когда кто-то в ваши структуры ручками залезет и там всё испортит?).

И да, имея
  jslike::var a;
  std::string b;
хочется-таки писать
  a = b;
а не
a = static_cast<char *>(b.c_str());

В конце-концов всё, что вы тут наворотили — это синтаксический сахар, а зачем он нужен, если при его использования у меня изо всех щелей будут лезть c_str и static_castы?

Почему у вас operator + не через operator += реализован? В результате если у вас есть
  jslike::var a = 2;
  jslike::var b = "test";
то
  a += b;
даёт undefined притом что
  a = a + b;
даёт 2test что, как по мне, ну… несколько странно.

Может быть не стоит писать статью «по мотивам» создания сырой и недоработанной библиотеки, а?
Признаком C++ является вменяемый код, в первую очередь.
А невменяемый код тогда на чём написан? ))
В конце-концов всё, что вы тут наворотили — это синтаксический сахар, а зачем он нужен, если при его использования у меня изо всех щелей будут лезть c_str и static_castы?

Не забывайте топикастер не я, и я не защищаю код, моё замечание было совершенно по другому поводу.
А невменяемый код тогда на чём написан? ))
А это уже криминалист нужен. Хорошо известно, что целеустремлённый Настоящий Программист может писать фортрановские программы на любом языке, но отсюда не следует, что всё, что не написано на C++ написано на фортране.
sizeof(wchar_t) implementation defined.
Я собственно как раз об этом. В моём конкретном случае — он четырёхбайтовый, а используется он для UTF-16 строк. Для этих целей в C++ есть char16_t.
Я что-то подобное делал на втором курсе, когда писал интерпретатор модельного языка, похожего на джаваскрипт=) Семестровая работа такая была.

В С++ вместо log() есть замечательный оператор <<. По-моему, это выглядит примерно так:
using namespace std;
class var{
    //...
    friend ostream& operator<<(ostream&, const var&);
    //...
}
ostream& operator<<(ostream& out, const var& value){
    out<<value.something;
    return out;
}
//...
var v;
cout<<"My var:"<<v<<endl;
Это что вообще я только что прочитал? Во первых зачем тут вообще JavaScript в заголовке, во вторых стоит самому нормально выучить, а уже затем пытаться учить других. И очередное изобретение класса строки, ну это вообще моветон.
Это скорее «Приключения C++ в мире JavaScript»
Ну и статья определенно кандидат в хаб «Ненормальное программирование» :)
Любопытная библиотека, спасибо!
Какие только велосипеды не изобретают люди, лишь бы не компилировать boost…

<code class="cpp">

var s1 = new char[10]; // Здравствуйте, утечки памяти

char a[2] = {'a','b'};
var s2 = a; // Здравствуй, "харт блидинг"

</code><pre>
лишь бы не компилировать boost

О какой конкретно библиотеке из буста идёт речь? Boost.Variant, к примеру, не подходит в качестве замены folly::dynamic (иначе бы dynamic не стали писать).

Ваши примеры кода не репрезентативны и представляют из себя типичный ССЗБ.

// Утечка памяти, виноват std::string!!!1
std::string heap_garbage(new char[10]);

char a[2] = {'a', 'b'};
// Читаем стек до первого нуля, виноват std::string!!!1
std::string stack_garbage(a);


Код автора ужасен во многих отношениях, но конкретно ваши претензии не обоснованы.
Первый пример обоснован по отношению к тому, что описано в статье (в библитеке на github'е конкретно этого ужаса нет, но есть много другого… интересного). Обратите внимание на то, что в этом чуде данные переданные как char * никуда не копируются. Так что без new вы туда char[10] передать не сможете. А вот new char[10] передать можно — но тогда да, память начнёт утекать.
без new вы туда char[10] передать не сможете

Почему? Передать можно, но если объект var выйдет из области видимости раньше соответствующего char[10], то будет UB. В противном случае должно «работать».

Просто хочется точности — проблема не в утечках памяти, а в том, что объект var в первоначальном определении не владеет данными, на которые ссылается, что ведёт к потенциальной возможности висячих указателей и, соответственно, UB.
В оправдание своих претензий могу лишь сказать, что я не пьющий и такого кода (как у меня, у вас и у автора) не писал уже поле полугода изучения С++. Даже подумать о таком не получается, для комментария специально фантазировал способы его порушить. Но когда только начал изучать, тогда и не такую кашу завариал.
«Как я перестал бояться и полюбил атомную верёвку для стрельбы в ногу».
По-моему, статья будет вредна для любого, кто начал изучать С++, ибо учит антипаттернам и велосипедам.
Сплошные шаги по граблям, буквально с первых строк.

Если хочется писать на С++ «с нуля», то придётся изучить управление памятью и временем жизни объектов.
Для яваскриптщика это определённый порог входа.
Если хочется сразу перейти к сути, то не надо издеваться над собой и читателями, используя голые типы char* и var*. Тем более, НЕПРАВИЛЬНО их используя.
Взяли бы std::string и std::vector, и было бы счастье.

За одно лишь «копирование посредством присваивания» надо выдать чугунную медаль.
Да, в гитхабе код выглядит свободным от ошибок (хотя я не вчитывался тщательно).
Но ведь статью читают дети! И они научатся! Плохому научатся, Карл!!!
В современном C/C++ "" строковые литералы имеют тип const char[N], так что код с char* не const-корректен. А ещё у конструкторов есть member initializer list, и стоило бы инициализации делать там, а не присваивание в теле конструктора.
И вообще ваш var, это union, tagged union, нет смысла в layout'е выделять память под все возможные хранимые типы.
И вообще ваш var, это union, tagged union, нет смысла в layout'е выделять память под все возможные хранимые типы.
Угу. И специально для этого в C++ можно описывать union'ы без имени. В версии на gihub'е это есть. Конечно это «чудесный» union с двумя полями — один int, другой data (а дальше — куча cast'ов), что, конечно, тоже непорядок (лучше было бы перечислить все типы с которыми реально происходит работа).
Реализация конструкторов через присваивание; operator=, возвращающий void… Кому-то было бы не плохо повторить основы C++.
UFO just landed and posted this here
Sign up to leave a comment.

Articles