Мне в первые годы изучения помогло прочитать книгу "Дизайн и эволюция C++", чтобы понять, почему язык получился такой сложный. Это дало какое-то базовое чувство языка и прибавило уверенности при его использовании.
По шаблонам самая полная информация, на мой взгляд, содержится в томике "Шаблоны C++. Справочник разработчика".
Мне кажется объяснение с ассемблером получилось сложнее, чем могло бы быть с https://cppinsights.io
В C++ лямбда функция - просто синтаксический сахар для класса с перегруженным оператором вызова `ret operator()(args...)`.
Если у лямбды пустой список захвата, то в сгенерированный класс добавляется оператор приведения к указателю на функцию: https://cppinsights.io/s/dab279d2
Если список захвата не пустой, то в сгенерированный класс добавляются соответствующие поля (ссылки для & или значения для =) и приведение к указателю на функцию невозможно, так как для вызова требуется экземпляр лямбды: https://cppinsights.io/s/5e323673
В описании std::vector небольшая неточность - стратегия роста емкости никак не указана в стандарте, так что это деталь реализации. В stdlibc++ и libc++ это удвоение размера, но вроде бы в Visual C++ используется множитель 1.5
А как ваш парсер JSON относится к Infinity или NaN значениям? Они ведь тоже не часть спецификации.
Желание усидеть на двух стульях понятно, но с плавающей точкой это невозможно. Либо побитовово точное хранение (a hex float оно и есть), либо человекочитаемое с потерей точности.
Мда, удовлетворение любопытства и написание велосипедов за счет работодателя может конечно разнообразить рутину и дать возможность выступить на конференции.
Но если действительно требуется сохранять точность, то можно просто почитать документацию и отформатировать float с флагом "a" - "converts floating-point number to the hexadecimal exponent notation."
Даже если поблизости нет C или С++ программиста, то вроде бы в Go можно сделать библиотеку, экспортирующую функции в C стиле, и использовать их с Node-API
Мне вот интересно стало, если Линусу не нравится реализация GCC, то где можно увидеть ядро, собранное альтернативным компилятором? И легко ли это сделать в принципе?
Проект, на котором я сейчас работаю, успешно собирается на С++ компиляторах от 3-х вендоров. Этот проект бесконечно мал, по сравнению с ядром Линукса, но и ресурсы на его разработку также бесконечно малы.
Вопрос: если новый компилятор GCC так не устраивает Торвальдса, то почему он ест этот кактус?
Со сменой типа параметров это может быть чуть проще. При желании можно добавить перегруженную версию ParseFromArray(const void* data, size_t size), опционально пометить ParseFromArray(const void* data, int size) как deprecated.
При смене возвращаемого типа тоже все решаемо, например какByteSize() -> ByteSizeLong().
Проблема в точке зрения авторов protobuf, что int для размеров это нормально.
Похоже здесь static_cast<size_t> был добавлен просто для того, чтобы задушить предупреждение компилятора о преобразовании знакового int в беззнаковый size_t.
Любовь Гугла к int для индексов и количества элементов в контейнере все время наталкивается на реальность стандартной библиотеки, где для этого используется size_t.
Но больше всего меня, как пользователя protobuf, раздражает их использование int для размера буфера. Ведь в Гугле уверены, что 640 кБ 2 ГБ хватит всем. Поэтому я останавливаюсь, вздыхаю и мысленно ругаюсь, когда приходится писать или читать кода вида
const std::string buf = protobufMessage.SerialzeAsString(); // ага, авторы protobuf решили, что std::string это самый подходящий тип для буфера с бинарными данными
protobufMessage.ParseFromArray(buf.data(), static_cast<int>(buf.size())); // избавляемся от предупреждения и верим что тут всегда будет меньше 2 ГБ
Это очевидно не так для node-based контейнеров типа std::map|set, где ссылка (и указатель тоже) на элемент в контейнере будет всегда действительна до удаления элемента.
Но недавно я узнал, что ссылка на элемент в std::unordered_map|set также действительна до удаления. Я был уверен, что они могут стать недействительными при вставке, если происходит rehashing. Однако, в документации говорится, что недействительными при вставке могут стать только итераторы:
If an insertion occurs and results in a rehashing of the container, all iterators are invalidated. Otherwise iterators are not affected. References are not invalidated.
Нет, внутри push_back(T&&) аргумент имеет имя _Val и является lvalue, которое нужно двигать дальше в emplace_back(), безусловно превращая _Val в rvalue с помощью std::move().
В примере с make_unique(Types&& ... _Args) параметры `_Args` могут быть как lvalue, так и rvalue, значит применяем std::forward<Types>(_Args)..., которая на этапе компиляции разберется, где l- и где r-value.
Неформально я запомнил это для себя так: если шаблонный параметр надо передать куда-то дальше, то используем для передачи std::forward():
template<typename T> func(T&& arg)
{
// SomeClass has a constructor for T
std::vector<SomeClass> vector;
vector.emplace_back(std::forward<T>(arg));
}
Для не шаблонной rvalue ссылки используем, если надо, std::move():
Полезно для начала было бы изучить опыт предшественников. Подход 1 поток на каждого клиента был признан несостоятельным еще в прошлом веке, и сформулирован как C10K problem в 1999 году.
Примерно с 2005 в C++ доступна boost.asio позволяющая писать кросс-платформенные асинхронные многопоточных сетевые приложения, в том числе и http серверы.
После массового перехода на C++11 стали доступны такие библиотеки как boost.beast, restinio. Наверно после принятия сопрограмм в C++20 появятся или уже появились новые версии.
Ngnix, насколько я помню, написан на C, совсем другом языке. На C++ сделан Envoy, если хочется посмотреть на устройство proxy сервера.
В hack_hren.cpp хотим по какой-то причине иметь доступ ко внутренносятм
#define private public
#include "hren.hpp"
...
Разные определения Hren в разных единицах трансляции -> ODR.
Еще в https://stackoverflow.com/a/27779038/1355844 утверждается, что само по себе переопределение ключевых слов — это UB, если в такой единице трансляции включается заголовочный файл стандартной библиотеки.
На практике такого пока не наблюдается, но не хотелось бы получить проблемы на ровном месте, даже в теории.
Почему бы в 21-м году 21-го века не воспользоваться достижениями прогресса, и не переложить заботы по управлению памятью на С++. Тогда с помощью https://github.com/pybind/pybind11 пишем на C++ модуль
#include <pybind11/pybind11.h>
int add(int i, int j) {
return i + j;
}
struct Pet {
Pet(const std::string &name) : name(name) { }
void setName(const std::string &name_) { name = name_; }
const std::string &getName() const { return name; }
std::string name;
};
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function which adds two numbers");
py::class_<Pet>(m, "Pet")
.def(py::init<const std::string &>())
.def("setName", &Pet::setName)
.def("getName", &Pet::getName);
}
и используем его в Python:
>>> import example
>>> example.add(1, 2)
3L
>>> p = example.Pet('Molly')
>>> print(p)
<example.Pet object at 0x10cd98060>
>>> p.getName()
u'Molly'
>>> p.setName('Charly')
>>> p.getName()
u'Charly'
Мне в первые годы изучения помогло прочитать книгу "Дизайн и эволюция C++", чтобы понять, почему язык получился такой сложный. Это дало какое-то базовое чувство языка и прибавило уверенности при его использовании.
По шаблонам самая полная информация, на мой взгляд, содержится в томике "Шаблоны C++. Справочник разработчика".
Мне кажется объяснение с ассемблером получилось сложнее, чем могло бы быть с https://cppinsights.io
В C++ лямбда функция - просто синтаксический сахар для класса с перегруженным оператором вызова `ret operator()(args...)`.
Если у лямбды пустой список захвата, то в сгенерированный класс добавляется оператор приведения к указателю на функцию: https://cppinsights.io/s/dab279d2
Если список захвата не пустой, то в сгенерированный класс добавляются соответствующие поля (ссылки для & или значения для =) и приведение к указателю на функцию невозможно, так как для вызова требуется экземпляр лямбды: https://cppinsights.io/s/5e323673
Пару недель назад читал пост на эту тему в канале Experimental chill
https://t.me/experimentalchill/211
Типа такого: https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition ?
В описании
std::vector
небольшая неточность - стратегия роста емкости никак не указана в стандарте, так что это деталь реализации. В stdlibc++ и libc++ это удвоение размера, но вроде бы в Visual C++ используется множитель 1.5Еще у Matthew Bentley, автора Colony, есть интересный документ про рост емкости для deque-like контейнера: https://plflib.org/matt_bentley_-_pot_blocks_bit_tricks.pdf
А как ваш парсер JSON относится к Infinity или NaN значениям? Они ведь тоже не часть спецификации.
Желание усидеть на двух стульях понятно, но с плавающей точкой это невозможно. Либо побитовово точное хранение (a hex float оно и есть), либо человекочитаемое с потерей точности.
Мда, удовлетворение любопытства и написание велосипедов за счет работодателя может конечно разнообразить рутину и дать возможность выступить на конференции.
Но если действительно требуется сохранять точность, то можно просто почитать документацию и отформатировать float с флагом "a" - "converts floating-point number to the hexadecimal exponent notation."
А почему не рассматривался 4-й вариант: написать Native Addon для Node.js использую https://nodejs.org/api/n-api.html#node-api ?
Даже если поблизости нет C или С++ программиста, то вроде бы в Go можно сделать библиотеку, экспортирующую функции в C стиле, и использовать их с Node-API
Да, действительно, только что clang-13 без проблем собрал ядро 5.15.6
Видимо clang стал еще более совместим с gcc и/или из ядра таки выпилили gcc-only код.
Мне вот интересно стало, если Линусу не нравится реализация GCC, то где можно увидеть ядро, собранное альтернативным компилятором? И легко ли это сделать в принципе?
Проект, на котором я сейчас работаю, успешно собирается на С++ компиляторах от 3-х вендоров. Этот проект бесконечно мал, по сравнению с ядром Линукса, но и ресурсы на его разработку также бесконечно малы.
Вопрос: если новый компилятор GCC так не устраивает Торвальдса, то почему он ест этот кактус?
И в чем смысл переводить статью годовалой давности? Так то C++20 уже принят и полным ходом разработка C++23
Со сменой типа параметров это может быть чуть проще. При желании можно добавить перегруженную версию
ParseFromArray(const void* data, size_t size)
, опционально пометитьParseFromArray(const void* data, int size)
как deprecated.При смене возвращаемого типа тоже все решаемо, например как
ByteSize()
->ByteSizeLong()
.Проблема в точке зрения авторов protobuf, что int для размеров это нормально.
Похоже здесь
static_cast<size_t>
был добавлен просто для того, чтобы задушить предупреждение компилятора о преобразовании знакового int в беззнаковый size_t.Любовь Гугла к int для индексов и количества элементов в контейнере все время наталкивается на реальность стандартной библиотеки, где для этого используется size_t.
Но больше всего меня, как пользователя protobuf, раздражает их использование int для размера буфера. Ведь в Гугле уверены, что
640 кБ2 ГБ хватит всем. Поэтому я останавливаюсь, вздыхаю и мысленно ругаюсь, когда приходится писать или читать кода видаconst std::string buf = protobufMessage.SerialzeAsString(); // ага, авторы protobuf решили, что std::string это самый подходящий тип для буфера с бинарными данными
protobufMessage.ParseFromArray(buf.data(), static_cast<int>(buf.size())); // избавляемся от предупреждения и верим что тут всегда будет меньше 2 ГБ
Это очевидно не так для node-based контейнеров типа std::map|set, где ссылка (и указатель тоже) на элемент в контейнере будет всегда действительна до удаления элемента.
Но недавно я узнал, что ссылка на элемент в std::unordered_map|set также действительна до удаления. Я был уверен, что они могут стать недействительными при вставке, если происходит rehashing. Однако, в документации говорится, что недействительными при вставке могут стать только итераторы:
https://en.cppreference.com/w/cpp/container/unordered_map/operator_at
Так что внимательное чтение документации иногда может быть полезно.
Нет, внутри
push_back(T&&)
аргумент имеет имя_Val
и является lvalue, которое нужно двигать дальше вemplace_back()
, безусловно превращая_Val
в rvalue с помощьюstd::move()
.В примере с
make_unique(Types&& ... _Args)
параметры `_Args` могут быть как lvalue, так и rvalue, значит применяемstd::forward<Types>(_Args)...
, которая на этапе компиляции разберется, где l- и где r-value.Неформально я запомнил это для себя так: если шаблонный параметр надо передать куда-то дальше, то используем для передачи
std::forward()
:template<typename T> func(T&& arg)
{
// SomeClass has a constructor for T
std::vector<SomeClass> vector;
vector.emplace_back(std::forward<T>(arg));
}
Для не шаблонной rvalue ссылки используем, если надо,
std::move()
:void func(std::string&& str)
{
std::vector<std::string> vector;
vector.push_back(std::move(str));
}
Полезно для начала было бы изучить опыт предшественников. Подход 1 поток на каждого клиента был признан несостоятельным еще в прошлом веке, и сформулирован как C10K problem в 1999 году.
Примерно с 2005 в C++ доступна boost.asio позволяющая писать кросс-платформенные асинхронные многопоточных сетевые приложения, в том числе и http серверы.
После массового перехода на C++11 стали доступны такие библиотеки как boost.beast, restinio. Наверно после принятия сопрограмм в C++20 появятся или уже появились новые версии.
Ngnix, насколько я помню, написан на C, совсем другом языке. На C++ сделан Envoy, если хочется посмотреть на устройство proxy сервера.
В hren.hpp определено
В hren.cpp лежит реализация, всё ок
В hack_hren.cpp хотим по какой-то причине иметь доступ ко внутренносятм
Разные определения Hren в разных единицах трансляции -> ODR.
Еще в https://stackoverflow.com/a/27779038/1355844 утверждается, что само по себе переопределение ключевых слов — это UB, если в такой единице трансляции включается заголовочный файл стандартной библиотеки.
На практике такого пока не наблюдается, но не хотелось бы получить проблемы на ровном месте, даже в теории.
Вроде бы это нарушение one definition rule, с теоретически побочным эффектом undefined behaviour
Почему бы в 21-м году 21-го века не воспользоваться достижениями прогресса, и не переложить заботы по управлению памятью на С++. Тогда с помощью https://github.com/pybind/pybind11 пишем на C++ модуль
и используем его в Python:
А можно посмотреть на сравнение с другими методами? Например, pull-request в https://github.com/miloyip/dtoa-benchmark было бы круто.