Я все таки не понял откуда повышение производительности тут. Только за счет того, что указатель на объект лежит рядом с указателем на VTable? Если так смотреть, то это ведь все равно не прямой вызов методов. Плюс если компилятор где-то мог сделать девиртуализацию, то тут уже не сможет.
Так это еще не production ready реализация, а только прототип?
В своем проекте тоже столкнулся с надобностью ивентной системы и она у меня уже прошла несколько итераций. В общем идеи все те же что и в этой статье. Тоже может статью написать? Для меня неожиданными моментами стали - время жизни подписчика событие. подписчик не должен переживать события и для этого нужна отписка в деструкторе. У меня для этого RAII объект Subscription. - время жизни события. при отписке от события нужно учитывать, что само событие может быть уже и не живо. - во время диспатчинга события, уничтожается само событие. У меня есть логика, которая по событию уничтожает и пересоздает объект владелец события. То есть диспатчинг события должен переживать само событие. - рекурсия - во время обработки события, заэмиттили то же самое событие. - ну про многопоточку понятно - если подписываться или отписываться во время диспатчинга события, может быть не очень хорошо.
Ну и по поводу реализации без virtual. Я по разному экспериментировал и пришел к тому, что у меня обработчик хранится в std::function. Можно искать и другие реализации, возможно более эффективные но скорее всего со своими ограничениями.
alignas(A) char data[sizeof(A)];
A* a = std::launder(reinterpret_cast<A*>(data));
Если A не POD тип? Если он наследуется от какого-то Base который имеет свои поля, который наследуется от некоего IAbstract у которого есть виртуальные методы реализуемые в A. Какие могут быть проблемы в c++17?
Доступ к приватным членам возможен только в контексте, где они видны компилятору , то есть внутри области, доступной на этапе компиляции. Попытка применить этот механизм к чужому классу без полного определения приведёт к ошибке компиляции — это гарантирует отсутствие произвольного доступа.
А я не понял как это работает. Если специализация идет не изнутри класса, то разве взятие адреса приватной переменной доступно? Адрес берется для инстанцирования шаблона, но в начале статьи ведь упоминалось, что нельзя взять адрес приватной переменной.
Шаблоны в C++ инстанцируются на этапе компиляции, и если специализация шаблона происходит в контексте, где приватный член доступен (например, внутри класса или через friend), то компилятор разрешит взять его адрес
class Foo{
private:
int a;
int b;
std::string& c;
};
Вообще я сам игрался с указателями на поля члены, делал на этом сериализацию. Для меня стали проблемой обращение к полям ссылкам и базовому классу. Код есть тут (с++17): https://github.com/aethernetio/aether-client-cpp/blob/main/aether/reflect/reflect.h Много пришлось подмазать макросами. Ну и в моем случае необходимость менять класс это фича, а не проблема.
Подумал было, что это уже должно работать в стандартном `std::tuple`, но как оказалось оно implementation defined. Плюс для msvc EBO оказывается нужно включать специально. Пример https://godbolt.org/z/dhEGfqnbz
У меня вопрос про [[no_unique_address]] А нет ли в компиляторах реализации своих кастомных атрибутов для до c++20 эпохи, так чтобы я мог наделать макросов и использовать [[no_unique_address]] фичу в c++17?
Про область видимости не совсем так. Обратите внимание https://godbolt.org/z/YTM1Kzq8r class X и Y объявляю внутри Foo, но X в той же области видимости что и Foo, а Y в области видимости внутри Foo.
С другой стороны, такая оценка нужна ровно в двух случаях: либо если вы собираетесь сделать дамп в памяти и хотите понять, сколько её надо резервировать, либо если вам надо строить внутренние таблицы релокации внутри такого дампа.
Ну звучит как достаточно важная оптимизация, но потенциально время самого GetBufLen может ее нивелировать.
У меня вопрос возник по поводу функций GetBufLen и SkipToEnd. В общем случае ведь, чтобы понять размер буфера требуемый для хранения структуры или пропустить ее в общем буфере. Нужно пройтись вглубь по всей структуре, разве это не будет равносильно по коду и времени сериализации/десериализации?
Пример есть класс, в котором вектор каких-то объектов.
Чтобы узнать размер буфера требуемого для хранения объекта этого класса, нужно узнать размер вектор, который известен, только в рантайме, и посетить каждый елемент этого вектора, так как внутри тоже могут быть динамические объекты. При пропуске, нужно прочитать из буфера размер вектора, затем прочитать каждый элемент, потому как из-за динамических объектов внутри, каждый элемент может быть разного размера и сколько пропускать не понятно.
Дополнительно про упакованные числа. Чтобы понять сколько байт нужно пропустить для числа в буфере, его ведь тоже нужно прочитать сначала?
Ну как зачем? Просто опытом поделится, вдруг кому-то будет полезно. Оно же тут не толкается, считаю пусть будет yet another статья или библиотека, чем не будет.
Вектор имеет размер и емкость. Тут речь про статическкую емкость, а размер как раз может меняться. Для std::vector ближайшим аналогом будет, если вы изначальное сделаете reserve и при добавлении элементов сами будете следить чтобы size() не превышал capacity(). Но std::vector память выделит в куче.
Я все таки не понял откуда повышение производительности тут. Только за счет того, что указатель на объект лежит рядом с указателем на VTable?
Если так смотреть, то это ведь все равно не прямой вызов методов. Плюс если компилятор где-то мог сделать девиртуализацию, то тут уже не сможет.
Так это еще не production ready реализация, а только прототип?
В своем проекте тоже столкнулся с надобностью ивентной системы и она у меня уже прошла несколько итераций. В общем идеи все те же что и в этой статье. Тоже может статью написать?
Для меня неожиданными моментами стали
- время жизни подписчика событие.
подписчик не должен переживать события и для этого нужна отписка в деструкторе. У меня для этого RAII объект Subscription.
- время жизни события.
при отписке от события нужно учитывать, что само событие может быть уже и не живо.
- во время диспатчинга события, уничтожается само событие.
У меня есть логика, которая по событию уничтожает и пересоздает объект владелец события. То есть диспатчинг события должен переживать само событие.
- рекурсия - во время обработки события, заэмиттили то же самое событие.
- ну про многопоточку понятно - если подписываться или отписываться во время диспатчинга события, может быть не очень хорошо.
Ну и по поводу реализации без virtual. Я по разному экспериментировал и пришел к тому, что у меня обработчик хранится в std::function. Можно искать и другие реализации, возможно более эффективные но скорее всего со своими ограничениями.
Статья то не про ассемблер, а про то как устроен машинный код.
Да нет, там в оригинале прямо написано
std::string. То есть проблема не в переводе, а в первоисточнике.не работает
У меня вопрос про конструкцию
Если
Aне POD тип? Если он наследуется от какого-тоBaseкоторый имеет свои поля, который наследуется от некоегоIAbstractу которого есть виртуальные методы реализуемые вA. Какие могут быть проблемы в c++17?А я не понял как это работает. Если специализация идет не изнутри класса, то разве взятие адреса приватной переменной доступно? Адрес берется для инстанцирования шаблона, но в начале статьи ведь упоминалось, что нельзя взять адрес приватной переменной.
В коде я не вижу ни "внутри класса" ни "friend".
А вот такое оно сможет переварить?
Вообще я сам игрался с указателями на поля члены, делал на этом сериализацию. Для меня стали проблемой обращение к полям ссылкам и базовому классу.
Код есть тут (с++17):
https://github.com/aethernetio/aether-client-cpp/blob/main/aether/reflect/reflect.h
Много пришлось подмазать макросами. Ну и в моем случае необходимость менять класс это фича, а не проблема.
Подумал было, что это уже должно работать в стандартном `std::tuple`, но как оказалось оно implementation defined. Плюс для msvc EBO оказывается нужно включать специально.
Пример https://godbolt.org/z/dhEGfqnbz
А статья есть про это?
У меня вопрос про [[no_unique_address]]
А нет ли в компиляторах реализации своих кастомных атрибутов для до c++20 эпохи, так чтобы я мог наделать макросов и использовать [[no_unique_address]] фичу в c++17?
https://godbolt.org/z/Mb57YsMjc странно. Тут работает.
https://godbolt.org/z/Eh53TGc5e это и аргументами функции работает
Про область видимости не совсем так.
Обратите внимание https://godbolt.org/z/YTM1Kzq8r
class X и Y объявляю внутри Foo, но X в той же области видимости что и Foo, а Y в области видимости внутри Foo.
Сам когда-то увидел в каком-то виде с cppcon. Не мог найти примеры и как это называется. Пришлось чатгпт мучать.
Есть еще вот такая штука
https://stackoverflow.com/questions/37744047/class-declaration-inside-function-parameter-list?utm_source=chatgpt.com
И можно например написать вот так
Ну звучит как достаточно важная оптимизация, но потенциально время самого GetBufLen может ее нивелировать.
У меня вопрос возник по поводу функций GetBufLen и SkipToEnd. В общем случае ведь, чтобы понять размер буфера требуемый для хранения структуры или пропустить ее в общем буфере. Нужно пройтись вглубь по всей структуре, разве это не будет равносильно по коду и времени сериализации/десериализации?
Пример есть класс, в котором вектор каких-то объектов.
Чтобы узнать размер буфера требуемого для хранения объекта этого класса, нужно узнать размер вектор, который известен, только в рантайме, и посетить каждый елемент этого вектора, так как внутри тоже могут быть динамические объекты.
При пропуске, нужно прочитать из буфера размер вектора, затем прочитать каждый элемент, потому как из-за динамических объектов внутри, каждый элемент может быть разного размера и сколько пропускать не понятно.
Дополнительно про упакованные числа. Чтобы понять сколько байт нужно пропустить для числа в буфере, его ведь тоже нужно прочитать сначала?
Ну как зачем? Просто опытом поделится, вдруг кому-то будет полезно. Оно же тут не толкается, считаю пусть будет yet another статья или библиотека, чем не будет.
Вектор имеет размер и емкость. Тут речь про статическкую емкость, а размер как раз может меняться. Для std::vector ближайшим аналогом будет, если вы изначальное сделаете reserve и при добавлении элементов сами будете следить чтобы size() не превышал capacity(). Но std::vector память выделит в куче.